I’ve a web server which has two PHP files, index.php
and controller.php
, the latter which handles all non-/ requests with a p
(for page) parameter, e.g.
/controller.php?p=some_page
The following nginx configuration works nicely:
server {
listen 80;
index index.php index.html;
server_name localhost;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /code;
location ~ .php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
However, I now want to clean up the URLs, and want /
to go to index.php
and /some_page
to go to /controller.php?p=some_page
.
Here’s the new configuration I’m trying:
server {
listen 80;
index index.php index.html;
server_name localhost;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /code;
# this is new, and makes index.php handle /
location / {
index index.php;
try_files $uri $uri/ =404;
}
location ~ .php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
# this is new, but doesn't work.
location @rewrites {
if ($uri ~* ^/([0-9a-z_]+)$) {
set $page_to_view "/controller.php?p=$1";
rewrite ^/([0-9a-z_]+)$ $scheme://$http_host/controller.php?p=$1 last;
}
}
}
The browser address bar shows this:
- http://localhost:8080/some_page (initial request)
- http://localhost:8080/controller.php?p=some_page (first redirect one second later)
- https://localhost/some_page/ (final redirect another second later)
So, it ends up on a URL which doesn’t have the original port, with a trailing slash, and using scheme https
.
What can I do to fix it?
PS It would be a bonus if trailing slashes didn’t matter (i.e. localhost:8080/some_page
and localhost:8080/some_page/
shows the same thing.
PS the port 8080 is just me locally testing via Docker, which maps container 80 to host 8080.
Update:
I’ve implemented Richard’s answers, and tried the suggested curl
:
$ curl -I http://localhost:8080/some_page
HTTP/1.1 301 Moved Permanently
Server: nginx/1.21.4
Date: Thu, 09 Dec 2021 15:07:11 GMT
Content-Type: text/html
Content-Length: 169
Location: http://localhost/some_page/
Connection: keep-alive
Note the lack of port 8080.
Update 2:
With nginx directive absolute_redirect off;
, I get this:
$ curl -I http://localhost:8080/some_page
HTTP/1.1 301 Moved Permanently
Server: nginx/1.21.4
Date: Thu, 09 Dec 2021 16:01:31 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: /some_page/
$ curl -I http://localhost:8080/some_page/
HTTP/1.1 404 Not Found
Server: nginx/1.21.4
Date: Thu, 09 Dec 2021 16:01:34 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
So, the question remains how a request to /some_page/
can serve up the response from /controller.php?p=some_page
2
Answers
I've landed on a configuration which works with the original links, but also the pretty URLs I was aiming for.
The key seems to be to not use a named location for the rewrite, but just match "everything else" with
location ~*
if the request isn't for/
or a PHP file.A named location (e.g.
location @rewrites
) is usually invoked from the last element of atry_files
statement.For example:
In your
location @rewrites
block, theif
statement is unnecessary, theset
variable is unused, and therewrite
statement will provoke an external redirect.Try: