Introduction

Welcome to the first lesson of Advanced NGINX Configuration and Monitoring! We're excited to embark on this journey with you as we explore the powerful capabilities of NGINX beyond basic web serving. Throughout this course, we'll dive deep into advanced configuration techniques that will help you build more efficient, scalable, and user-friendly web applications.

In this lesson, we'll focus on URL rewriting and redirects, essential techniques for creating clean, search engine-friendly URLs and maintaining backward compatibility as your application evolves. By the end of this lesson, you'll understand how to transform query-based URLs into elegant path-based ones, handle legacy URLs gracefully, and create dynamic routing patterns using regular expressions.

Understanding URL Rewriting and Redirects

Before we dive into the code, let's clarify what URL rewriting and redirects accomplish and why they matter. Modern web applications often need to present clean, readable URLs to users while maintaining compatibility with older URL structures. For example, instead of showing /products?id=123, we might want to display /products/123.

URL rewriting and redirects serve different but complementary purposes:

  • Redirects tell the client's browser to make a new request to a different URL, which is visible to the user.
  • Rewrites happen internally on the server, transforming one URL pattern into another without the client knowing.

Note: With a rewrite, the correct page will open without redirecting. But here's the crucial distinction: Opening the correct page is not the same as having the correct URL. When using a rewrite, the URL in the browser's address bar remains unchanged—the user still sees the original URL they requested, even though the server is processing a different internal path. This is in contrast to a redirect, where the browser's URL updates to reflect the new location.

Together, these techniques help us maintain a clean URL structure, support legacy paths, and create more intuitive navigation for our users.

Setting Up the Server Block

Let's start by establishing our basic NGINX server configuration. We'll configure a server that listens on port 3000 and sets up the foundation for our URL handling logic:

This configuration creates a simple HTTP server that responds with plain text by default. The server listens on port 3000 and is ready to accept requests on localhost. We've set default_type text/plain to simplify our responses for demonstration purposes.

Implementing Legacy Path Rewrites

Now let's handle a common real-world scenario: our application has changed its URL structure, but we need to support old URLs that users might have bookmarked. We'll use the rewrite directive to permanently redirect old product URLs to the new format:

This directive performs several important functions:

  • The pattern ^/old-products/(\d+)$ matches URLs like /old-products/123.
  • The parentheses (\d+) capture the numeric product ID.
  • The replacement /products/$1 uses the captured ID in the new URL.
  • The permanent flag returns a 301 status code, telling browsers and search engines this is a permanent change.

When a user visits /old-products/456, NGINX automatically redirects them to /products/456, ensuring backward compatibility while encouraging adoption of the new URL structure.

Handling Query Parameters with Redirects

Many legacy systems use query parameters for routing, such as /products?id=123. Let's create a cleaner experience by redirecting these to path-based URLs:

This location block handles requests to exactly /products (note the = modifier). Here's what happens:

  • The special variable $arg_id contains the value of the id query parameter.
  • If $arg_id exists, we issue a 301 redirect to the clean URL format /products/123.
  • If no id parameter is provided, we return a 400 Bad Request with an error message.

This approach transforms /products?id=123 into /products/123, creating URLs that are more readable and SEO-friendly.

A Note on Using if in NGINX: You may have heard that if directives are discouraged in NGINX configuration (sometimes referred to as "If is Evil"). While this is generally true due to unexpected behavior in many contexts, using if with return is one of the safe cases. The directive immediately stops processing, avoiding the common pitfalls associated with . For more complex conditional logic involving query parameters, consider using the directive.

Creating Dynamic Routes with Regex

With our redirects in place, we now need to actually handle the clean URLs and serve content. We'll use a regular expression location block to capture the product ID from the URL path:

The tilde ~ indicates this is a regex-based location block. The pattern ^/products/(\d+)$ matches URLs like /products/123, and the captured number is available as $1. For demonstration, we're returning a simple response that includes the product ID.

In a real application, you might use this captured value to query a database or proxy to an upstream application server. Let's see what this produces when we request /products/789:

The response confirms that NGINX successfully extracted the product ID from the URL and made it available for processing.

Adding a Fallback Handler

Finally, we need a catch-all handler for requests that don't match our specific location blocks. This ensures proper error handling for invalid URLs:

The try_files directive attempts to serve files in order:

  • First, it tries to serve a file at the exact path $uri.
  • If that doesn't exist, it tries to serve it as a directory $uri/.
  • If both fail, it returns a 404 Not Found error.

This fallback ensures that any request not handled by our specific product routes receives an appropriate response rather than hanging or producing unexpected behavior.

Conclusion and Next Steps

In this lesson, we've built a comprehensive URL handling system using NGINX. We learned how to redirect legacy URLs with the rewrite directive, transform query-based URLs into clean paths using location blocks and conditionals, extract data from URLs with regular expressions, and provide appropriate fallback handling for unmatched routes.

These techniques form the foundation of modern web routing and are essential for maintaining clean, maintainable URL structures in production applications. The combination of redirects, rewrites, and dynamic pattern matching gives us the flexibility to evolve our application's URL structure while maintaining backward compatibility.

Now it's time to put these concepts into practice! In the upcoming exercises, you'll have the opportunity to implement these URL rewriting and redirect patterns yourself, solidifying your understanding through hands-on experience.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal