NGINX Friendy URLs

I’m having problems getting friendly urls to work with my set up.

I’m using MODx Revolution v2.8.0 on a dedicated server powered by NGINX using Debian 10 and a Virtualmin control panel. I have used the Nginx Server Config found here: https://docs.modx.com/current/en/getting-started/friendly-urls/nginx, but it has not worked for me.

I set up an About Us resource page in Manager and can only see the page at /index.php?=2 (but only on Firefox - not on Google Chrome or Edge). I cannot see the page at /about

I’ve got Use Friendly URLs, Use Strict Friendly URLs and Use Friendly Alias Path all set to ‘Yes’. The below is my NGINX server config file. Can someone please take a look and suggest where they can see I am going wrong with this?

Here is my configuration file:

server {
        listen 80;
        server_name domain .com www.domain .com;
        root /home/domain/public_html;
        index index.php;
        client_max_body_size 30M;
        location / {
                #root /home/domain/public_html;
                if (!-e $request_filename) {
                        rewrite ^/(.*)$ /index.php?q=$1 last;
                }
        }
        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                fastcgi_ignore_client_abort on;
                fastcgi_param  SERVER_NAME $http_host;
        }

        location ~ /\.ht {
                deny  all;
        }
}

server {
	server_name domain .com www.domain .com;
	listen 00.0.000.000;
	root /home/domain/public_html;
	index index.html index.htm index.php;
    client_max_body_size 30M;
	access_log /var/log/virtualmin/domain.com_access_log;
	error_log /var/log/virtualmin/domain.com_error_log;
	fastcgi_param GATEWAY_INTERFACE CGI/1.1;
	fastcgi_param SERVER_SOFTWARE nginx;
	fastcgi_param QUERY_STRING $query_string;
	fastcgi_param REQUEST_METHOD $request_method;
	fastcgi_param CONTENT_TYPE $content_type;
	fastcgi_param CONTENT_LENGTH $content_length;
	fastcgi_param SCRIPT_FILENAME /home/domain/public_html$fastcgi_script_name;
	fastcgi_param SCRIPT_NAME $fastcgi_script_name;
	fastcgi_param REQUEST_URI $request_uri;
	fastcgi_param DOCUMENT_URI $document_uri;
	fastcgi_param DOCUMENT_ROOT /home/domain/public_html;
	fastcgi_param SERVER_PROTOCOL $server_protocol;
	fastcgi_param REMOTE_ADDR $remote_addr;
	fastcgi_param REMOTE_PORT $remote_port;
	fastcgi_param SERVER_ADDR $server_addr;
	fastcgi_param SERVER_PORT $server_port;
	fastcgi_param SERVER_NAME $server_name;
	fastcgi_param PATH_INFO $fastcgi_path_info;
	fastcgi_param HTTPS $https;
    
	location ~ \.php(/|$) {
		try_files $uri $fastcgi_script_name =404;
		fastcgi_pass unix:/var/php-nginx/160000000000092.sock/socket;
	}
	fastcgi_split_path_info ^(.+\.php)(/.+)$;
	listen 00.0.000.000:443 ssl;
	ssl_certificate /home/domain/ssl.combined;
	ssl_certificate_key /home/domain/ssl.key;
}

I don’t have an answer for you, but I added some back-ticks around your code to preserve the formatting and make it easier to read. I hope you don’t mind.

1 Like

Thanks for taking a look and doing that bobray.

1 Like

Just checking the basics, but in the configuration you pasted it has “domain” generically. Are you passing traffic on a specific domain? I would expect to see the actual host name there. If you just changed it for the post that’s fine, but wanted to make sure.

1 Like

Thanks for asking jaredfhealy. I changed it for the post to conceal the real domain name, for the usual obvious security purposes. I did the same with the listening IP (00.0.000.000, etc).

So, typing out a reply… I got to the very end and realized your rewrite is missing the arguments “&$args” which might be causing some issue. Also, I think that the format presented in the docs is outdated. Even within Modx Cloud hosting, they use the try_files directive and fall back on a named location. I have mine in two files, but you could also have the named location defined inline where I am doing the include modx.conf.

Note: After doing all this setup I decided, “Nah, I don’t want to manage the server.” And I moved it to A2hosting. :slight_smile:

I followed this Digital Ocean setup guide for Nginx:

So, I’m not sure if it will be helpful or not, but here’s the NGINX setup that I used that worked fine. I notice you have quite a few extra parameters in your FastCGI config. I am using basically the vanilla/recommended starting configuration which has only 2 lines to process PHP. I didn’t have any issues, but you may have those details in there for a reason as I am fairly new to Nginx configurations.

This was on a server that had multiple MODX sites, so I set up two additional configuration files that I pulled into each /sites-available conf: restrictions.conf and modx.conf.

I also used Certbot to make the certificate generation much easier. So that portion of the config is set automatically when you run the Certbot process.

server {

    server_name domain.com domain2.com www.domain3.com sub.domain.com;
    root /var/www/grv0003/sites/$host;
    index index.php index.html index.htm;

    include /etc/nginx/global/restrictions.conf;
    include /etc/nginx/global/modx.conf;

    access_log /var/log/nginx/grv0003/access.log;
    error_log /var/log/nginx/grv0003/error.log;

    # pass PHP scripts to FastCGI server
    location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
    }

    location / {
        try_files $uri $uri/ @modx-rewrite;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($scheme = 'http') {
        return 301 https://$host$request_uri;
    }

    # Remove www
    if ($host = www.$host) {
        return 301 https://$host$request_uri;
    }

    listen 80;
    listen [::]:80;
    server_name domain.com domain2.com www.domain3.com sub.domain.com;
    return 404;
}

And then the modx.conf file that is included from the /global folder I created. This allows additional sites to pull in shared configuration but is not needed. This could be defined within the same /sites-available conf file.

location /core/ {
    return 404;
}

location @modx-rewrite {
    rewrite ^/(.*)$ /index.php?q=$1&$args last;
}
1 Like

I really appreciate your time in looking at this. The actual rewrite ‘args’ appears on line 10 with rewrite ^/(.*)$ /index.php?q=$1 last; which obviously did not work. I will try the various suggestions in the code you presented and post what I find.

The other ‘outdated’ and somewhat convoluted code perform various functions and is there to provide backward compatibility and security.

I think it would only affect the manager possibly or paging if you were using that, but what I meant was on line 10 you are missing $args which contains the rest of the query parameters. The $1 just contains the path that was matched by the regex:

rewrite ^/(.*)$ /index.php?q=$1 last;
-------------- Should be ------------------
rewrite ^/(.*)$ /index.php?q=$1&$args last;
2 Likes

I stepped away from the internet over the New Year and holiday period but thanks, once again jaredfhealy, for helping me understand things clearer.

I like the idea of using include files as I do want to host more sites using Modx, once I have the /subfolder configuration working. Could you help me further by showing me what you’ve got in the following files:

include /etc/nginx/global/restrictions.conf;
include /etc/nginx/global/modx.conf;

Like you I’m a relative newcomer to nginx (a few months ago) but I get the concept and learning it makes using it more fun, once I untangle certain parts of it.

I unfortunately don’t have the server anymore. But you can refer to this Modx Support article that has some Security rules. Scroll down to point #6 in the list. I was using that restrictions file to include things like this. I only had a couple items in there I was testing out.

1 Like

Thanks for the link. I’ve seen the content of what typical global restrictions.conf and modx.conf files look like. You’ve placed yours in a ‘global’ sub-folder. My setup did not generate a /global sub-folder automatically. I guess I have to create it manually…

Hi there,
this is interesting for DigitalOcean case, i.e I have tried several option for config but it never loads the friendly url, always not found page.

Please see my test domain nginx config for MODX revolution 2.8.5

server {

    root /var/www/t******/pub;
    index index.html index.php;
    server_name test.sergeymi*****.com;


      location / {
            absolute_redirect off;
            try_files $uri $uri/ @modx-rewrite;
#             try_files $uri $uri/ /index.php?q=$1&$args;

        }

        location @modx-rewrite {
                rewrite ^/(.*)$ /index.php?q=$1&$args;
        }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;

   }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
    }


    location ~ /\.ht {
        deny all;
    }

    location ~ ^/(\.(?!well_known)|_build|_gitify|_backup|core|config.core.php) {
    rewrite ^/(\.(?!well_known)|_build|_gitify|_backup|core|config.core.php) /index.php?q=doesnotexist;
}

# only allow manager access on cms.example.com
set $mgrcheck $host$request_uri;
if ($mgrcheck ~* "((?<!cms.)example\.com/manager)") {
    rewrite /manager /index.php?q=doesnotexist;
}

Anyone is OK to suggest what could be wrong this time? :sos: