automad icon indicating copy to clipboard operation
automad copied to clipboard

Use `$_SERVER` rather than `getenv()` for server request vars

Open michaellenaghan opened this issue 2 years ago • 12 comments

When I moved from my development machine to my production host, index.php files started appearing in the URLs.

Using Automad's excellent debugging capabilities, I tracked the problem down to here.

My host doesn't set a SERVER_SOFTWARE environment variable.

In fact, it turns out that using environment variables for such things is wrong.

I found an interesting 2016 thread here. The long and short of it is that Symfony was using getenv() for certain kinds of things, and no longer does. (To be clear: it no longer uses getenv() for server request vars; it does for other things not related to server request vars.) Laraval is the same.

It seems that the vars listed on the $_SERVER page should only be accessed through $_SERVER, and never through getenv()?

In the meantime, my workaround was to add this to index.php:

putenv('HTTP_HOST='.$_SERVER['HTTP_HOST']);
putenv('HTTP_X_FORWARDED_HOST='.$_SERVER['HTTP_X_FORWARDED_HOST']);
putenv('HTTP_X_FORWARDED_SERVER='.$_SERVER['HTTP_X_FORWARDED_SERVER']);
putenv('HTTPS='.$_SERVER['HTTPS']);
putenv('SERVER_NAME='.$_SERVER['SERVER_NAME']);
putenv('SERVER_SOFTWARE='.$_SERVER['SERVER_SOFTWARE']);
putenv('SCRIPT_NAME='.$_SERVER['SCRIPT_NAME']);

(I'm not suggesting that my workaround is a solution; in fact, as given that approach won't work in some environments. The point was to show you the list of server request vars that might be relevant to the issue.)

michaellenaghan avatar Jun 08 '22 20:06 michaellenaghan

There are 23 uses of getenv() in 6 files. I checked them against the keys listed on the $_SERVER page; it looks like all of them should be switched to $_SERVER. (Btw, a couple of keys, like SERVER_NAME, are currently accessed both ways.)

Let me know if you'd like me to tackle this.

(Btw, just for completeness, my original list missed one key: SERVER_PORT.)

michaellenaghan avatar Jun 09 '22 19:06 michaellenaghan

Hi, thanks for reporting this. The advantage of getenv is that it doesn't require a check whether a key exists. In that sense one could argue it is even the better solution. But I understand your trouble and will take a look at it.

However, there is an easy way to make the index.php disappear from the URL. In case the automatic detection fails, you can set the AM_INDEX constant to an empty string in the config.

marcantondahmen avatar Jun 11 '22 15:06 marcantondahmen

In that sense one could argue it is even the better solution. But I understand your trouble and will take a look at it.

Marc, this isn't "my trouble." It's about the wrong approach and the right approach. There's no sense in which one could argue that the wrong approach is the better solution!

I sent you a thread in which the Symfony developers admitted they were wrong, and changed their code. (There's a lot of detail in that thread. It's worth a careful read. I followed links and looked at the source code they were talking about.)

After finding that, I took the time to verify that both Laravel and Symfony exclusively use $_SERVER for server request vars today.

Here is Grav, another flat-file PHP-based CMS. Same thing.

Here is Kirby, another flat-file PHP-based CMS. Same thing.

If you like the convenience of what getenv() provides you can create a small wrapper around $_SERVER that works the same way.

michaellenaghan avatar Jun 11 '22 20:06 michaellenaghan

_SERVER is the correct solution. As it currently is, automad cannot be run behind distributed transparent caching proxies. Which is a bit sad, since it's quite nice.

I haven't tested if the putenv solution above will work with ha-proxy / varnish & a cdn, but I'll try to get to it.

poetaster avatar Feb 14 '23 14:02 poetaster

I haven't tested if the putenv solution above will work with ha-proxy / varnish & a cdn, but I'll try to get to it.

No, that is not sufficient. I'm not sure what is happening here, but I can varnishlog the POST requests. I checked to make sure that no form / session cookies are being unset (are not), but login does not work. It does with the same setup on a drupal install.

poetaster avatar Feb 14 '23 14:02 poetaster

Hi, so it looks that switching to $_SERVER would be the only working solution, right? I haven't tested it in the environment you described though. But I guess it is not a big deal to replace getenv() entirely.

It will probably be fixed in the next major release.

marcantondahmen avatar Feb 14 '23 14:02 marcantondahmen

One other thing came into my mind. There is the AM_BASE_PROXY constant that can be defined in the config in order to address issues with URL rewriting for pages behind proxies. Maybe you could give that one a quick try.

marcantondahmen avatar Feb 14 '23 15:02 marcantondahmen

One other thing came into my mind. There is the AM_BASE_PROXY constant that can be defined in the config in order to address issues with URL rewriting for pages behind proxies. Maybe you could give that one a quick try.

I'll try this later this evening. Thanks!

poetaster avatar Feb 14 '23 16:02 poetaster

One other thing came into my mind. There is the AM_BASE_PROXY constant that can be defined in the config in order to address issues with URL rewriting for pages behind proxies. Maybe you could give that one a quick try.

Errr, what should it be set to? In my drupal configs I noted 127.0.0.1 ?

poetaster avatar Feb 14 '23 17:02 poetaster

127.0.0.1 ? that value does not work ;)

poetaster avatar Feb 15 '23 09:02 poetaster

After taking a closer look, it turned out that it needed more than changing from getenv() to $_SERVER. That actually has really nothing todo with it at all, as I mentioned before. However, I agree that a streamlined use of $_SERVER is more consistent here as well.

The actual issue is that the automatic determination of the base URL is not working correctly when running behind a proxy. That basically breaks the app. In the beginning it was not meant to run in such a scenario and also was not really tested therefore.

However, I have just added the ability to run behind a proxy. The feature will be included in the next major release.

marcantondahmen avatar Feb 16 '23 10:02 marcantondahmen

However, I have just added the ability to run behind a proxy. The feature will be included in the next major release.

Ah, that's great news. I host a lot of smaller sites on IPv6 only virts and proxy IPv4 with a transparent cache, so that would make those scenarios automadic :) Thanks for all your work!

poetaster avatar Feb 16 '23 12:02 poetaster