I am writing a wordpress plugin that redirects the user. for some reason, wp_redirect does not work as think it should, namely: it seems not to redirect the user at all, unless I put a die command in directly after. here is some code:
switch($_GET['wp_favorites_tax'])
{
case 'post_tag':
wp_redirect(add_query_arg('tag', $_GET['wp_favorites_term'], get_bloginfo('url')));
die();
break;
case 'category':
wp_redirect(add_query_arg('cat', $_GET['wp_favorites_term'], get_bloginfo('url')));
die();
break;
default:
wp_redirect(get_bloginfo('url'));
die();
}
It seems really strange to me that I should have to tell my script to die so that the redirect can work. I have also tried a basic
header("Location: $location);
to similar ends, ie: it still requires the die() in order to work. really perplexing me. Thanks.
This is normal behaviour for header() under certain circumstances, I don't remember the details why right now (I think it had to do with output buffering). I'm sure somebody will be able to shed some light on this.
Anyway, terminating the script's execution after a header("Location...") is something one should get used to anyway - the sending of a header is just a request to the browser to please move to a new location, but will not prevent the sending of the rest of the document, which can have catastrophic consequences when used in login systems (the user gets a header redirect because they're not logged in, but also gets served all the possibly confidential data in the document body).
Always, always call die() or exit after doing a header redirect.
Usually, a HTTP redirect contains a message body for clients which don’t follow the header automatically. Try this in Opera (Options: Network).
Since wp_direct() may be called before the translation is loaded, WordPress offers you the chance to add our own (localized) message body.
In short: I think, the current solution is the best if you care about I18n.
Related
I need to redirect users if I don't want them to be able to access a certain page. How reliable is header('Location: ../acc/login.php'); for example? Can browsers ignore 302 errors, and is this the right way? Thanks in advance!
It depends a lot what you're trying to do. Technically spoken, header() is somewhat reliable. Only somewhat, because many PHP users have problems with it and to not get it to work.
PHP will prevent it from working if output has been already send to the browser. A drastic example:
<protected page content here>
<?php
header('Location: login-first.php');
exit();
?>
This would not work at all. You would eventually see even an error message with a warning.
Headers - by design - need to be send out before any other content (response body). They can not be send any longer if the response body has already started and PHP can't help you then in that case.
However, if you send headers before the response body, that function will work. Also the risk obviously to mess something up is not that drastic any longer, too:
<?php
header('Location: login-first.php');
exit();
?>
<protected page content here>
You can rely on header(), but make sure you called die(), exit() or return after that. Otherwise, script will continue its execution, which is potential security issue.
The browser can ignore header('Location: '); forwarding.
That is why you should always return after a call to a header() forward so the rest of your code does not execute should the browser not honor the forwarding.
It is the correct way to do things tho.
I would send the header command and then the exit command "exit()" (to stop running the php code on the server) before displaying the rest of the page. This way the user would never be sent the page content even if they ignored the 302 redirection.
And yes the user can ignore the 302 redirection:
http://www.webmasterworld.com/html/3604591.htm
header is 100% reliable.
However header('Location: ../acc/login.php') will be evaluated in the browser to a real location on your website, and ../acc/login.php wil not form a url that is valid!
I have a very basic question... In a php function I'm redirecting if a value returns FALSE otherwise continuing the code.
flibbertigibbet()
{
$value = true_or_false();
if ($value == FALSE)
{
//redirect to another controller method
}
//the rest of the code
}
What happens after the redirect? Does the code break or does a little execute if say.. the redirect takes longer to load?
Is it good practice to use exit() after redirects?
The code will continue to execute - not just when the redirect takes longer, but each time nd through to the end.
Whether you use exit() depends on whether or not you want the rest of the code to be executed. You might set a header() and send a new address, but you can still execute things afterwards - like updating a database or some other bits.
I normally think it is best to do all your required updating and send the header() right at the end of a page - makes debugging much easier and more intuitive.
Just setting a redirect header does nothing on its own. ALWAYS exit or die after setting a Location header (unless you know exactly what you are doing)
It really depends on what you mean by redirect. Yes, it will never run the rest of the code if it's something like:
header("Location: http://newurl.com");
Otherwise, unless there's a good reason for it not to come back (infinate loops, you're trying to include content which is taking too long and have not set a timeout, you use an exit()...), then the rest of the code will be run as soon as the if condition code has finished running.
So I know the general rule of thumb is after doing a header redirect in PHP, you should call exit() to avoid having extra code running, but I want to know if you put code after the redirect header, if it will always run?
I was doing some research on various ways of tracking referrals in Google Analytics and came across this post: Google Analytics Tips & Tricks – Tracking 301 Redirects in Google Analytics
It recommends doing something like this:
<?
Header( “HTTP/1.1 301 Moved Permanently” );
Header( “Location: http://www.new-url.com” );
?>
<script type=”text/javascript”>
var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
document.write(unescape(“%3Cscript src=’” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));
</script>
<script type=”text/javascript”>
try {
var pageTracker = _gat._getTracker(“UA-YOURPROFILE-ID”);
pageTracker._trackPageview();
} catch(err) {}</script>
From the way I've always understood the header() function, it's up to the browser and it can run the redirect whenever it wants to. So there's no guarantee the JavaScript would actually begin or finish executing prior to the redirect occurring.
PHP's documentation for the header() function indicates the reason for exiting after a redirect is to "make sure that code below does not get executed when we redirect." That doesn't sound like they guarantee all following code will run, just that it could happen.
Regardless, I found a different way to actually manage the tracking, but I wanted to see if I could find out how exactly header() worked in this situation..
Thanks for your help.
Using the header function in PHP only adds to the headers of the response returned by the server. It does not immediately send any data and does not immediately terminate the connection. Any code after the header call will be executed.
In particular, it's a good idea to add a response body even after doing a 301 redirect so that clients that do not support the redirect also get some descriptive response. Infact according to the HTTP 1.1 specification Section 10.3.2 -
Unless the request method was HEAD, the entity of the response SHOULD
contain a short hypertext note with a hyperlink to the new URI(s). If
the 301 status code is received in response to a request other than
GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.
It's a race condition. Once the redirect header is sent to the browser, the browser will close the current connection and open a new one for the redirect URL. Until that original connection is closed and Apache shuts down the script, your code will continue to execute as before.
In theory, if there was a sufficiently fast connection between the client/server, and there was no buffering anywhere in the pipeline, issuing the header would cause the script to be terminated immediately. In reality, it can be anywhere between "now" and "never" for the shutdown to be initiated.
The HTML after your Location line doesn't run inside PHP; it would run in the browser. It's up to the browser whether or not to execute the Javascript that you've included on that page; PHP has nothing to do with it.
To me, the PHP docs imply that any PHP below the header() when you send a redirect will continue to run. But it 'runs' in the PHP interpreter, dumping JS to the browser. There's no relation between what it says in the PHP docs and whether or not the JS gets run by the browser.
EDIT:
Well, as Anupam Jain pointed out, looks like that browsers do not terminate connection without getting the response body and it sounds sensible. So i rethinked my answer
That doesn't sound like they guarantee all following code will run
Exactly More likely it's a warning in case there is some sensible code that shouldn't be executed. A private page contents for example. So, beside sending header you have to also make sure that no sensitive content were sent and exit looks like quite robust solution. So, I'd phrase it as "make sure that sensible code below does not get executed when we redirect."
So there's no guarantee the JavaScript would actually begin or finish executing prior to the redirect occurring.
Exactly It seems it has nothing to do with script execution but rather with browser's will to execute anything after getting 3xx response. I think I'm gonna test it, but you can test it as well too.
I have noticed that the code does still execute and multiple headers based on if statements can cause a "redirect loop error". i made it a habit to now add in die("Redirecting..."); after every header redirect and have not see the problem persist.
Quick question, I noticed that on some of my header directors I was getting some lag while the header processed. Is using return standard after using headers? Also if you use a header on pages you don't want directly accessed, such as processing pages will return; stop that processing even if the page is not directly accessed? IF return is a good idea would it be better to use exit()?
header("Location: ......"); exit; is a fairly common pattern.
You do not need to supply return; after calling header but I know some people use the convention of supply exit; after header call to ensure the code below will not execute during a redirect.
Keep in mind you can use header() for other things besides Location: redirects:
header("Content-type: image/jpeg"); // for example
The reason you would exit after a header redirect is, any content output after a header() redirect, will (most likely) not be seen by the browser.
More importantly you wouldn't want any code to be executed after a header() redirect, so calling exit() after a redirect is good practice.
When you send the header, it is but a mere advisory to the client(the browser) that you think they should request another url instead. However, nothing can stop them from not following your recommendation. They can continue reading more data from the current url, if your server keeps feeding it to them. This is why you generally see php code that calls exit() after sending a redirect header, because if you stop outputting more data, there is nothing for them to read.
Aside from keeping them from reading unintended data, there's other reasons:
Maybe it's just plain senseless for the rest of the script to continue executing, wasting resources.
Maybe runtime errors would occur if the script were to continue(ex, there were missing variables, or a db connection failed).
Maybe logic errors would occur if the script were to continue(ex, user input validation/authentication failed).
It's up to the client to determine what to do after an header("Location: ...").
Any code after header() will be executed regardless. Putting an exit(); just after the header is a safeguard and is required for securing your site.
If you have some candy after header("Location: ..."), the only thing the browser have to do is to ignore the request. Then it'll be clear as day. With exit(); you're stopping execution of the page and hopefully there are no other attack vectors to your app!
I just read this article on tdwtf.com. Generally, it describes an archiving bot destroying things because it ignores headers. I then realized that I don't know how to do security in a page WITHOUT headers. Therefore my question is:
What security measures can i take besides using headers?
I develop mostly in php, so I'm familiar with header("Location: ") function. But what else is out there?
Ideally I'm looking to replace the logic of
if (!$something_important) header("Location: somehereharmless.php");
with something else (more) secure?
This one works pretty well
if (!$something_important) {
header("Location: somehereharmless.php");
exit();
}
Even if it's bot, so it doesn't respect Location, you will call an exit so the execution flow is halted anyway, so no harm
header: location is fine, as long as you include an exit at the end.
You might also want to include a link or something.
I usually use something like this:
<?php
function redirect($url)
{
header('Location: ' . $url);
exit('Redirecting you to: ' . $url . '');
}
redirect('somepage.php');
?>
This way people can't bypass the redirect, and know that they should be redirected.
[Edit]
Also, always use POST when deleting stuff. It is very easy to create a fake GET (for example <img src="http://www.example.org/action.php?do=SetAsAdmin&userid=MyUserId" />).
Make sure all your gets are idempotent
Idempotent means that doing same request many times has the same effect as doing it once.
I'd say that if you have a PHP script that performs some action which only, say, logged-in users should be able to perform, you must put the check for being logged in right there in the very same script, so you can look at it at a glance and see that the code is secured. My rule is that there are only two valid patterns for protecting secured code:
if (user_is_authorized()) {
// restricted code here
}
or Alekc's
if (!user_is_authorized()) {
// send headers or whatever if you want
exit();
}
// restricted code here
To be perfectly honest, I was rather shocked... or at least disappointed... when I read that article - I can't understand how someone came to the conclusion that a website could be secured with HTTP headers. A header is nothing more than some text that your server sends out. It's an instruction that may be followed or ignored by the client at will (at least, you have to think about it that way for security purposes). As far as I'm concerned, outgoing (response) HTTP headers are pretty much useless for security. (This is not counting things like HTTP authentication, where you send a header and get one back in response... but in that case it's the contents of that reply header that you base your security on, not the header you sent out.)
The reason for the incident reported in the link you provided was the missing exit; statement after the header();. The bot can't do any harm if the script stops.-
if (!$something_important) {
header("Location: somehereharmless.php");
//close all your db connections and other stuff you need to end..parhaps calling a function?
die("If the redirect doesnt start in 3 seconds, please click here");
}
Your solution is
<?php
die($errormessage);
Die will just halt your script, not go through start, don't collect any data that you shouldn't.
Addition to Alekc's answer. If you have many header("Location:") statements and the person qualifies for them all. The last one will fire.
if($foo && $bar)
{
header("Location: somehereharmless.php");
}
if($foo && $baz)
{
header("Location: someotherplace.php");
}
So if that user has all 3 variables set, he will get redirected to someotherplace.php. Unless you have an exit(); or a die(); after the header();