Nginx how to; Server Side Include (SSI) debugging

Server Side Includes (SSI) are a great feature of Nginx allowing you to cache core content of a page but dynamically replace any blocks of the page that are dynamic, for example login links / welcome text. But they can sometimes hold some gotchas, so heres a post on debugging SSI or debugging Nginx configs in general.

I spent a morning debugging on Nginx when SSI's weren't acting as expected - they were missing! including the stubs and I was losing markup on the page. When dealing with SSI's here are two key rules:

  • SSI's must be well formed - otherwise funny things happen!
  • Debugging isn't always easy, but patience is key!

SSI's must be well formed

Funny things happen when your SSI syntax is malformed! But finding the cause isn't always easy.

What's wrong with this snippet?

<!--# block name="default_message" -->  <p>Sorry, this feature is currently unavailable</p><!-- endblock --><!--# include virtual="/dynamic/footer-links" stub="default_message" -->

The results of the above snippet is all content / markup after it is truncated!

Debugging SSI's

Firstly, turn off Silent Errors in your config like so:

ssi_silent_errors off;
You should see in the browser any errors outputted as "[an error occurred while processing the directive]" if there has been an error with the SSI.

If you are using a proxy - check the proxy access logs to ensure that the requests are being made by Nginx! If they are ensure that they are rendering the expected output.

Next step is to check what Nginx is doing, turn on debugging and logging of SSI's in your Nginx config:

error_log   /var/log/nginx/error.log debug;    log_subrequest  on;

The error log will now start to report all logic steps and actions for requests made to it - they are quite extensive! You are looking for all SSI includes:

... [debug] 1545#0: *2 ssi include: "/dynamic/user_status_bar/"

If your SSI include isn't listed - then check your markup! It probably is malformed like mine above - I was missing a # from the endblock tag it should have read:

<!--# block name="default_message" -->  <p>Sorry, this feature is currently unavailable</p><!--# endblock --><!--# include virtual="/dynamic/footer-links" stub="default_message" -->

That simple typo cost me a morning, so check your markup! What actually was happening was that Nginx got all the SSI but it couldn't find the endblock and silently errored and discarded the rest of the content.

Nginx how to; Url cleaning - removing multiple slashes

Carrying on with my short posts on configuring Nginx, heres an easy tip to ensure that all urls are clean, multiple slashes are removed and redirected to the cleaned url eg: http://example.com//search/ and will be redirected to: http://example.com.

**Updated Sept 15th

# Remove any multislashes in the url
  # $uri is a cleaned version of the url
  # so we test against the $requested_uri
  set $clean_uri        $uri$is_args$args;
  if ( $clean_uri != $request_uri ) {
    rewrite ^/(.*)      $scheme://$host/$1    permanent;
  }

This will stop you serving the same content on multiple urls which has SEO implications. In the past I have written middleware to do the check and redirects, but as the Nginx Proxy Module uses the cleaned $uri the middleware never sees the multiples slashes and doesn't know without looking at X-Headers that it is serving an invalid url.

* Wondering why the redirect doesn't redirect to itself? The regular expression just captures everything after the first slash! Well the rewrite module also uses the cleaned $uri!

** Update: June 24th

It seems that you can get into a nasty redirect loop, so if your just cleaning double slashes you can use this:

# Remove any multislashes in the url
    if ($request_uri ~* "\/\/") {
      rewrite ^/(.*)      $scheme://$host/$1    permanent;
    }

 

** Update: Sept 15th

Jon Topper found a bug with the previous fix - http://www.example.com//?// would cause a redirect loop. He posted in the comments a fix and I'm putting here so its easier to read. Thanks Jon.

# Remove any multislashes in the url
    # Tested against:
    #   http://www.example.com//
    #   http://www.example.com/w//w//w//
    #   http://www.example.com//?//
    set $test_uri $scheme://$host$request_uri;
    if ($test_uri != $scheme://$host$uri$is_args$args) {
        rewrite ^ $scheme://$host$uri$is_args$args? permanent;
    }