1

I'm testing a site which is build with CodeIgniter on a development Vagrant box (Ubuntu TLS 14 with nginx 1.8.1 and PHP 5.6 with php-fpm) and CodeIgniter just seems to fail with nginx.

After several trials and errors and gathering bits and pieces from other similar issues, I was able to make partial redirect. This site has URL structure based on regional ISO codes like: http://example.com/REGION/language so if I'm browsing site from US it redirects to http://example.com/US/en and if Europe than http://example.com/EU/en. There are at least 4-5 such combinations by using GeoIP database and CodeIgniter 301 redirects.

My nginx configuration goes as below:

# Default server configuration
server {

    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;

    # Add index.php to the list if you are using PHP
    index index.php index.html index.htm index.nginx-debian.html;

    server_name _;

    location / {

        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
        #try_files $uri /index.html index.php =404;
        #try_files $uri $uri/ /index.php;
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {

        # regex to split $uri to $fastcgi_script_name and $fastcgi_path
        fastcgi_split_path_info ^(.+\.php)(/.+)$;

        # Check that the PHP script exists before passing it
        try_files $fastcgi_script_name =404;

        # Bypass the fact that try_files resets $fastcgi_path_info
        # see: http://trac.nginx.org/nginx/ticket/321
        set $path_info $fastcgi_path_info;
        fastcgi_param PATH_INFO $path_info;

        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;

        #FastCGI conf
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        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_NAME $fastcgi_script_name;
        fastcgi_param REQUEST_URI $request_uri;
        fastcgi_param DOCUMENT_URI $document_uri;
        fastcgi_param DOCUMENT_ROOT $document_root;
        fastcgi_param SERVER_PROTOCOL $server_protocol;
        fastcgi_param HTTPS $https if_not_empty;

        fastcgi_param GATEWAY_INTERFACE CGI/1.1;
        fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

        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;

        # PHP only, required if PHP was built with --enable-force-cgi-redirect
        fastcgi_param REDIRECT_STATUS 200;
    }

    # deny access to .htaccess files, if Apache's document root concurs with nginx's one
    location ~ /\.ht {
        deny all;
    }

    location /exmple {

        root /var/www/html/;
        index index.php;

        location ~ ^/example/(.+\.php)$ {

            client_max_body_size 4M;
            client_body_buffer_size 128k;
            root /var/www/html/;
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(.*)$;

            # With php5-fpm:
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
            include /etc/nginx/fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        }

        location ~* ^/example/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt)) {

            root /var/www/html/;
        }

        # CI rewrite rules
        if ($host ~* ^www\.(.*)) {

            set $host_without_www $1;
            rewrite ^/(.*)$ $scheme://$host_without_www/$1 permanent;
        }

        # Canonicalize codeigniter url end points and if your default controller is something other than "welcome" you should change the following
        if ($request_uri ~* ^(/pages(/view)?|/index(.php)?)/?$) {

            rewrite ^(.*)$ / permanent;
        }

        # removes trailing "index" from all controllers
        if ($request_uri ~* index/?$) {

            rewrite ^/(.*)/index/?$ /$1 permanent;
        }

        # removes trailing slashes (prevents SEO duplicate content issues)
        if (!-d $request_filename) {

            rewrite ^/(.+)/$ /$1 permanent;
        }

        # removes access to "system" folder, also allows a "System.php" controller
        if ($request_uri ~* ^/system) {

            rewrite ^/(.*)$ /index.php?/$1 last;
            break;
        }

        # unless the request is for a valid file (image, js, css, etc.), send to bootstrap
        if (!-e $request_filename) {

            rewrite ^/(.*)$ /index.php?/$1 last;
            break;
        }

        # catch all
        error_page 404 /index.php;
    }

}

So when I browse it from localhost like http://192.168.33.15/example/ it redirects to http://192.168.33.15/example/INT/en which is default if a valid location is not found in GeoIP database where example is a subdirectory within the nginx root. No errors up to here but nginx is not redirecting properly and interprets INT/en as folder so it pops out with a 404 Not Found error.

I've gone through many forum posts here on Serverfault, Stackexchange and other online resources but none of them have helped and since there is not much documentation on CodeIgniter or nginx sites either this seems like the right place to ask.

0

Found a much simpler solution. Following what CodeIgniter does behind the scenes for clean URL's; in Apache this rewrite works by sending each request as http://example.com/index.php?/controller/method which is then processed by CodeIgniter so your config.php should look like

$config['base_url'] = 'http://example.com'
$config['index_page'] = '';
$config['uri_protocol'] = 'REQUEST_URI';

To achieve the same in nginx you'd have to rewrite each request in identical way. Fine tuned my nginx configuration with:

location /example {

    root /var/www/html/;
    index index.php;

    location ~ ^/example/(.+\.php)$ {

        fastcgi_buffer_size 128k;
        fastcgi_buffers 256 4k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
        root /var/www/html/;
        include /etc/nginx/snippets/fastcgi-php.conf;
    }

    location ~* ^/example/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt)) {
        root /var/www/html/;
    }

    # Enforce SSL
    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }

    # CI rewrite rules
    if ($host ~* ^www\.(.*)) {
        set $host_without_www $1;
        rewrite ^/(.*)$ $scheme://$host_without_www/$1 permanent;
    }

    if ($request_uri ~* ^(/example/(US|EU|INT)/(en|de)(.*))?$) {
        rewrite ^/(.*)$ /example/index.php?/$1 last;
    }

    if ($request_uri ~* ^(/example/(US|EU|INT)/(en|de)/(.*))?$) {
        rewrite ^/(.*)$ /example/index.php?/$1 last;
    }

    # catch all
    error_page 404 /index.php;
}

The missing link existed in nginx rewrite debug log so if you want to know about your rewrite rules and whether they are matched against your rule set add this to your nginx configuration:

server{
  ...
  rewrite_log on;
  error_log /var/log/nginx/error.log info;
  ...
}

And nginx will give you each request log with a match or no match against your PCRE regex. From here on you can tweak your configuration. I had to prepend path (directory) example in nginx configuration file for rewrite rules to work as expected.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.