Replying with a 301 Moved Permanently
instead of a 404 not found


I recently moved my blog to a new host, so several objects don’t exist any more. Unfortunately, this will lead to plenty of 404 not found, instead of giving users access to the website. So I decided to replace all not found with redirects to a certain page, it might be any kind of sorry-page or the default page. It’s up to you.

The problem

I don’t know how to change all the HTTP response using Citrix ADC (NetScaler). So I have to re-write the HTTP.RES.STATUS to a redirect and set the header location.

Which kind of redirect?

There are several kinds of redirects:

307 and 308 are similar to 301 and 302, but do not allow the HTTP method to change. That’s not what we want, we just redirect to a different page. 303 is used after an HTTP post. So it would either be a 301 or a 302.

If content won’t be there in future, a 301 is better than a 302, as it instructs search engines like Ecosia, Google or Bing to remove the previous content, follow the redirect and put all the new context into their index. A 302 is the opposite: Content is temporarily unavailable but will be back soon, so crawlers of search engines won’t follow the redirect. Because of this, I would suggest using a 301, a permanent redirect.

The solution

We need two policies, one changing the response status, the other to set the header.


Setting the response status

The response status (HTTP.RES.STATUS can be changed using the action of type REPLACE:

Citrix ADC / NetScaler: send a 301 - the action to set the response status to 301

add rewrite action rw_act_301_Status replace HTTP.RES.STATUS "\"301 Moved Permanently\""

Setting the location

The location is transferred in a header called location, so we simply have to set this header:

Citrix ADC / NetScaler: send a 301 - the action to set the location to redirect

add rewrite action rw_act_301_location_falsch replace "HTTP.RES.HEADER(\"Location\")" "\"\""


There are two approaches:

  • Create two policies and bind them to the HTTP response side.
  • Create a policy label and invoke it.

The policy expression

No matter, which solution you prefer, the policy expression will always look like this:
"HTTP.RES.STATUS.EQ(404) && HTTP.REQ.HEADER(\"Accept\").CONTAINS_ANY(\"media_type\")"

  • see if the response status is a 404
  • see if the requested content had been of any kind of media type desired (text/plain, text/html, text/php)

So my first action would be: create the pattern-set …

Citrix ADC / NetScaler: send a 301 - the pattern set
add policy patset media_type
bind policy patset media_type "text/plain" -index 1
bind policy patset media_type "text/php" -index 2
bind policy patset media_type "text/html" -index 3

… and the policy. I prefer using policy labels, so I get everything in one place. The policy invoking the label got the policy expression filtering the right 404s, the policies within the label simply get a true.

Citrix ADC / NetScaler: send a 301 - the policy invoking the policy label

Binding it all together

I bind it, using a policy label, as described here:
Citrix ADC / NetScaler: send a 301 - the policy label
add rewrite policylabel rw_res_label_301 http_res
bind rewrite policylabel rw_res_label_301 rw_pol_301_location 100 NEXT
bind rewrite policylabel rw_res_label_301 rw_pol_301_Status 110 NEXT

invoking the policy label

Citrix ADC / NetScaler: send a 301 - invoking the policy label

bind lb vserver -policyName rw_pol_call301Label -priority 100 -gotoPriorityExpression NEXT -type RESPONSE -invoke policylabel rw_res_label_301

Like always: I’d be glad to get feedback on this. Tell me, if you have a better idea, tell me if you like it. (And tell me if I did a mistake)


About the author

Johannes Norz

Johannes Norz is a Citrix Certified Expert on Networking and a Citrix Technology Advocate.

He frequently works for Citrix international Consulting Services and several education centres round the globe.

Add comment

By Johannes Norz

Recent Posts

Recent Comments