Rewriting all URLs to a single PHP controller on nginx - docker

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

A named location (e.g. location #rewrites) is usually invoked from the last element of a try_files statement.
For example:
location / {
index index.php;
try_files $uri $uri/ #rewrites;
}
In your location #rewrites block, the if statement is unnecessary, the set variable is unused, and the rewrite statement will provoke an external redirect.
Try:
location #rewrites {
rewrite ^/([0-9a-z_]+)$ /controller.php?p=$1 last;
}

I've landed on a configuration which works with the original links, but also the pretty URLs I was aiming for.
server {
listen 80;
index index.php;
server_name localhost;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /code;
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;
}
location ~* {
rewrite ^/([0-9a-z_]+)/?$ /controller.php?p=$1 last;
}
}
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.

Related

nginx serving only / but not any other files

I have two docker containers as below:
nginx ==> working as proxy web server (nginx web server)
dist ==> working as a php-fpm container
And this is my dist.conf:
server {
server_name dist.me.com;
root /var/www/html;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass dist:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/dist_error.log;
access_log /var/log/nginx/dist_access.log;
}
The issue is if I enter dist.me.com, it shows my index.php contents fine. But if I enter dist.me.com/index.php or dist.me.com/index2.php, I get error 404 Not Found.
I tried changing some values of conf file but it did not help me.
Both index.php and index2.php exist in /var/www/html path.
Your existing dist.conf is designed to block URIs ending with .php. The internal directive prevents a location from being directly accessed, and there is also a location which explicitly returns 404 for any URI ending with .php.
You need to change the location rule, remove the internal directive, and delete the location block which follows it.
For example, replace location ~ ^/index\.php(/|$) { ... } and location ~ \.php$ { ... } with the single location block as follows:
location ~ \.php(/|$) {
fastcgi_pass dist:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}

Nginx config file is not configured properly. "This site can’t be reached"

I'm fairly new to Nginx and web servers in general.
My setup is docker, Nginx, PHP/laravel, +let's encrypt.
I have this Nginx config file:
server {
listen 80;
server_name www.example.io;
return 301 https://example.io$request_uri;
}
server {
listen 80;
server_name example.io;
return 301 https://example.io$request_uri;
}
server {
listen 443 ssl;
server_name example.io;
ssl_certificate /etc/nginx/certs/example.io.pem;
ssl_certificate_key /etc/nginx/certs/example.io.key;
index index.php index.html;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /var/www/public;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
gzip_static on;
}
}
I'm pretty sure my certificates are not what the problem is. because they do work when I have a single port 80 server and it has server_name example.io www.example.io but in that case, I'm unable to access the website through example.io as secure. while the www.example.io is secure. it also acts like two different websites. I believe the cookies in one do not implement in the other.
What I'm trying to achieve is, I wish to redirect both www.example.io and example.io to https://example.io

Nginx fails to load text/css (Run under docker)

I have a Nginx that runs behind docker.
Here's my .conf file
events {
worker_connections 2048;
}
http {
default_type application/octet-stream;
server {
listen 80;
index index.php index.html;
root /var/www/public;
location / {
try_files $uri /index.php?$args;
}
location ~ \.css {
include /etc/nginx/mime.types;
add_header Content-Type text/css;
}
location ~ \.js {
add_header Content-Type application/x-javascript;
}
location ~ ^/(assets/|css/|js/|index.html) {
root /var/www/public;
index index.html;
access_log off;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
}
I've followed the solution here:
Nginx fails to load css files
I also have tried the following:
Put include /etc/nginx/mime.types; under http {
I've checked mime.types that it has the correct value as follows:
text/css css;
The result that i am getting from Chrome dev console is this:
Accept-Ranges: bytes
Content-Length: 52806
Content-Type: text/plain
Content-Type: text/css
Date: Thu, 20 Sep 2018 09:49:35 GMT
ETag: "5ba0bfef-ce46"
For some reason, Nginx loads it as text/plain and text/css altogether.
This is the Nginx version that I am using:
Server: nginx/1.15.3
EDIT
Interestingly enough, if i do curl -v like this, it is being treated as text/css
Can anyone point me in the right direction? Thanks

Nginx configuration for Wordpress in a directory

I have a Rails app configured with nginx with its root at /var/www/apps/example/current/public. It's accessed at https://www.example.com. That all works great. I decided to add a blog, and I wanted to go with Wordpress. I installed it in a different directory on the server, /var/www/apps/blog, and I want to be able to access it by going to https://www.example.com/blog
Below is my nginx config:
server {
listen 443;
ssl on;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/certs/example.com.key;
server_name www.example.com;
root /var/www/apps/example/current/public;
passenger_enabled on;
rails_env production;
client_max_body_size 100M;
client_body_buffer_size 256k;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /blog {
root /var/www/apps;
location ~ [^/]\.php(/|$) {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
I can navigate to example.com/blog and it takes me there without any issues. The problem comes when I enable pretty URLs and try to view a post. It circumvents the /blog directory and hits my Rails app, ending up with a 404. I see the following error in /var/log/nginx/error.log:
2016/06/30 17:24:32 [error] 7035#0: *20 "/var/www/apps/blog/hello-world/index.html" is not found (2: No such file or directory), client: 199.27.128.198, server: www.example.com, request: "GET /blog/hello-world/ HTTP/1.1", host: "www.example.com", referrer: "https://www.example.com/blog/"
Adding an index index.php directive doesn't fix the problem, it merely then states that /var/www/apps/blog/hello-world/index.php is not found instead.
Any ideas? I'm stumped.
try_files $uri=404; is not enough for pretty permalinks.
To enable pretty permalinks, you need to change it to:
location /blog {
root /var/www/apps/blog;
try_files $uri $uri/ /index.php?$args;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Or, try non-root try_files redirect:
location /blog {
try_files $uri $uri/ /blog/index.php?$args;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Here's a quick guide setting up wordpress with rails

Nginx causes 301 redirect if there's no trailing slash

I'm running nginx in a Virtual Machine using NAT and I'm having redirection issues when I access it from the host machine.
Works as expected
http://localhost:8080/test/index.htm: works.
http://localhost:8080/test/: works.
Doesn't work as expected
http://localhost:8080/test: redirects to http://localhost/test/ . This is not what I want. (notice it strips the port number)
What I've tried
Based on what I've googled, I tried server_name_in_redirect off; and rewrite ^([^.]*[^/])$ $1/ permanent;, both with no success.
My default.conf:
server {
listen 80;
server_name localhost;
# server_name_in_redirect off;
location / {
root /usr/share/nginx/html;
index index.html index.htm index.php;
}
location ~ \.php$ {
# rewrite ^([^.]*[^/])$ $1/ permanent;
root /usr/share/nginx/html;
try_files $uri =404;
#fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/tmp/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
I posted a possible solution to this problem on serverfault; reproduced here for convenience:
If I understand the question correctly, you want to automatically serve, without using a 301 redirect, http://example.com/foo/index.html when the request is for http://example.com/foo with no trailing slash?
Basic solution that works for me
If so I've found this try_files configuration to work:
try_files $uri $uri/index.html $uri/ =404;
The first $uri matches the uri exactly
The second $uri/index.html matches a directory containing the index.html where the last element of the path matches the directory
name, with no trailing slash
The third $uri/ matches the directory
The fourth =404 returns the 404 error page if none of the preceding patterns match.
Taken from Serverfault answer
My updated version
If you add in the server block:
index index.html index.htm;
And modify try_files to look like this:
try_files $uri $uri/ =404;
It should work too.
A somewhat simpler solution, that worked for me, is to disable absolute redirects with absolute_redirect off; as in the following example:
server {
listen 80;
server_name localhost;
absolute_redirect off;
location /foo/ {
proxy_pass http://bar/;
}
If I run curl on on http://localhost:8080/foo, I can see that the Location header in the redirect HTTP response is given as /foo/ and not http://localhost/foo/.
$ curl -I http://localhost:8080/foo
HTTP/1.1 301 Moved Permanently
Server: nginx/1.13.8
Date: Tue, 03 Apr 2018 20:13:28 GMT
Content-Type: text/html
Content-Length: 185
Connection: keep-alive
Location: /foo/
From that, I assume any web-browser would do the right thing with the relative location. Tested on Chrome and it works fine.
try :
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm index.php;
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$uri/ permanent;
}
}
}
Try changing
server_name localhost;
# server_name_in_redirect off;
to
server_name localhost:8080;
server_name_in_redirect on;

Resources