🚀 Executive Summary

TL;DR: The challenge of serving city-specific content from a single domain is solved by mapping user IP addresses to locations. This can be achieved through server-side GeoIP databases or by offloading the logic to cloud-native edge services like CDNs and DNS routing.

🎯 Key Takeaways

  • Web servers lack inherent geographical awareness, requiring GeoIP ‘translators’ to map IP addresses to physical locations.
  • Manual IP mapping (e.g., Nginx `geo` module) is a brittle, non-scalable ‘duct tape’ solution suitable only for tiny, known user sets.
  • The ‘permanent fix’ involves using a dedicated GeoIP module (e.g., `ngx_http_geoip2_module` with MaxMind GeoLite2) for accurate, comprehensive IP-to-location mapping, requiring monthly database updates.
  • Cloud-native solutions offload GeoIP logic to the edge (CDNs like Cloudflare, DNS like AWS Route 53, or ALBs), offering scalability, speed, and reduced origin server load, albeit with potential vendor lock-in.
  • Always implement a `default` option in GeoIP maps to provide a fallback for unknown locations and prevent 404 errors, ensuring a good user experience.

Is it possible to have a landing page for different cities?

Redirecting users to city-specific content from a single domain can be a headache, but it’s solvable. Learn three practical methods, from quick server-side fixes with Nginx to scalable, cloud-native solutions using GeoIP at the edge.

One Domain, Ten Cities: A Senior Engineer’s Guide to Geo-Targeted Landing Pages

I still remember the “CityLaunch” project from a few years back. 3 AM, the night before a major marketing campaign goes live. The frantic Slack message comes in from a junior dev: “The landing page isn’t working! Everyone in the London office is seeing the New York page!” We had one domain, `citylaunch.io`, and a mandate to show a unique experience for ten different metro areas. The dev had built ten beautiful pages, but the server, `prod-web-01`, was blissfully unaware of geography and just served up the default `index.html` to everyone. It’s a classic problem: your application is smart, but your infrastructure is dumb. We fixed it with a hacky, middle-of-the-night patch, but it taught me a valuable lesson: routing isn’t just about URLs, it’s about user context.

The “Why”: Servers Don’t Carry Passports

At its core, the problem is simple. When a user in Berlin visits your website, your web server (be it Nginx, Apache, or Caddy) only sees an IP address. By itself, that IP address is just a string of numbers. The server has no inherent knowledge that 81.2.69.142 is likely someone in London. To make intelligent, location-based decisions, you have to give the server a “translator”—a tool to map that IP address to a physical location. Once you have that city, state, or country, you can rewrite the request, serve a different file, or redirect the user entirely.

So, let’s look at a few ways to build that translator, from the quick fix to the “right” way.

Solution 1: The ‘Duct Tape’ Fix (Manual IP Mapping)

This is the “It’s 3 AM and this just has to work” solution. It’s brittle, a pain to maintain, but it’s fast and requires no external modules. You basically create a manual list (a “map”) of known IP blocks for the cities you care about and tell your web server what to do with them.

Let’s say you want to show a special page for your London office, and you know their public IP range is 81.2.69.0/24. You could add this to your Nginx config:

http {
    # Map the client IP to a variable, let's call it $landing_page
    geo $landing_page {
        default         /index.html;      # Everyone else gets the default
        81.2.69.0/24    /london.html;     # Our London office IP block
        # You could add more here... but don't. Please don't.
    }

    server {
        listen 80;
        server_name example.com;

        location = / {
            # Internally rewrite the request for the root path
            rewrite ^ /.$landing_page last;
        }

        # ... other server config ...
    }
}

Why it’s “Duct Tape”: You have to manually find and update IP ranges for every city. These change, and you’ll never get them all. This is only viable for a tiny, known set of users, like internal offices, not the general public.

Solution 2: The Permanent Fix (The GeoIP Database)

This is the proper way to do it on a single server or traditional VM. You use a dedicated module and a professional GeoIP database (like the free GeoLite2 database from MaxMind) to do the IP-to-location mapping for you.

First, you’d need to install the Nginx GeoIP2 module (`ngx_http_geoip2_module`). Then, you download the database files and point your Nginx config to them.

http {
    # Load the GeoIP databases
    geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb {
        # Map the remote IP to a city name variable
        $geoip2_city_name source=$remote_addr city names en;
    }

    # Map the city name to a specific page
    map $geoip2_city_name $landing_page {
        "London"    /london.html;
        "Tokyo"     /tokyo.html;
        "New York"  /new-york.html;
        default     /index.html;
    }

    server {
        listen 80;
        server_name example.com;

        location = / {
            # Rewrite based on the city found in the map
            rewrite ^ /.$landing_page last;
        }
    }
}

Why it’s better: This is far more accurate and comprehensive than a manual list. It handles millions of IP ranges for you. The only ongoing work is running a cron job to update the .mmdb database file once a month so your data stays fresh.

Pro Tip: Always have a default option in your map. GeoIP lookups can fail or return an unknown location. If you don’t have a fallback, users from unlisted cities will get a 404 error, which is a terrible user experience.

Solution 3: The ‘Cloud Native’ Way (Offload to the Edge)

If you’re running on a modern cloud stack, managing GeoIP databases on individual servers is kind of old-school. Why handle that logic on your origin server when you can have a global network do it for you at the “edge”—long before the request ever hits your infrastructure? This is faster for the user and simpler for you to manage.

This approach uses services like a Content Delivery Network (CDN) or a cloud provider’s DNS routing.

Method How It Works
Cloudflare Page Rules / Workers Cloudflare automatically identifies the user’s country. You can create a simple Page Rule (e.g., “If incoming request is from United Kingdom, forward to `https://example.com/uk`”) or write a more complex Worker script to handle city-level logic by inspecting the cf.city object. The redirect happens at their edge, instantly.
AWS Route 53 Geolocation Routing This is a DNS-level solution. You can create DNS records that point to different servers (or load balancers) based on the user’s location. For example, requests from Europe could be routed to a server pool in Frankfurt (`eu-central-1`), while requests from North America go to a pool in Virginia (`us-east-1`). Each server pool would be configured to serve its respective local content.
Application Load Balancer (ALB) Rules Some load balancers, like AWS ALB, allow you to create listener rules based on HTTP headers. A CDN at the front (like Cloudfront) can add a header like CloudFront-Viewer-Country: US to the request, and the ALB can then route the request to a specific target group configured for US content.

Why it’s the future: This decouples the location logic from your application servers. Your app servers can be “dumb” again; they just serve the content they’re told to. This approach is more scalable, often faster for end-users, and reduces the processing load on your origin. The downside? It usually involves vendor lock-in and can be more complex to set up initially.

Ultimately, the right choice depends on your stack. But for that panicked junior dev at 3 AM? We used a variation of Solution 2 and got the campaign launched. The next week, though, we started planning our migration to an edge-based solution.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ How can I implement geo-targeted landing pages on a single domain?

Geo-targeting can be implemented server-side using Nginx with the `ngx_http_geoip2_module` and a GeoIP database like MaxMind GeoLite2, or by leveraging cloud-native edge services such as Cloudflare Page Rules/Workers, AWS Route 53 Geolocation Routing, or Application Load Balancer rules.

âť“ What are the primary differences between server-side GeoIP and cloud-native edge solutions?

Server-side GeoIP requires managing databases and processing on your origin server, offering direct control. Cloud-native edge solutions offload location logic to CDNs or DNS, providing faster user experiences, greater scalability, and reduced origin load, but may introduce vendor lock-in and initial setup complexity.

âť“ What is a critical consideration to prevent errors when setting up GeoIP mapping?

It is critical to always include a `default` option in your GeoIP map configuration. This ensures that users from unlisted or unknown locations receive a fallback page (e.g., `/index.html`) instead of a 404 error, maintaining a consistent user experience.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading