A GCP load balancer isn’t just a traffic director; it’s an active participant in shaping application behavior, often in ways that surprise you.

Let’s say you have a backend service serving static assets and you want to route requests for /images/* to a specific, optimized compute instance group, while everything else goes to your main application servers.

Here’s how you’d set that up using traffic rules (specifically, URL maps and path matchers in GCP). Imagine your main application is on us-central1-a in an instance group named app-servers, and your image optimization is on us-central1-b in image-servers.

First, you need a URL map. This is the core component that inspects incoming requests and decides where to send them.

gcloud compute url-maps create my-app-url-map \
  --default-service app-servers \
  --global

This sets up a basic map where all traffic, by default, goes to your app-servers backend. Now, we add a path matcher to define specific routing rules.

gcloud compute url-maps add-path-matcher my-app-url-map \
  --default-service app-servers \
  --path-matcher-name image-matcher \
  --default-path-rule \
  --path-rules "/images/*=image-servers" \
  --global

Let’s break this down. add-path-matcher creates a new set of rules within our URL map. image-matcher is just a name for this set of rules. --default-service app-servers means if none of the specific rules match, it falls back to app-servers. --path-rules "/images/*=image-servers" is the magic: any request whose path starts with /images/ will be sent to the image-servers backend service.

The default-path-rule flag is crucial here. When you add a path matcher, GCP needs to know what to do with paths that don’t match any of your specific rules. By including --default-path-rule and specifying --default-service app-servers again, you’re ensuring that traffic not matching /images/* still goes to your main application. If you omitted this, and a request didn’t match /images/*, it would be dropped.

You can also specify host rules. If you have api.example.com and images.example.com, you could route them differently.

gcloud compute url-maps create my-host-map \
  --default-service app-servers \
  --global

gcloud compute url-maps add-host-rule my-host-map \
  --host-rule-name api-rule \
  --hosts "api.example.com" \
  --path-matcher-name api-path-matcher \
  --global

gcloud compute url-maps add-path-matcher my-host-map \
  --path-matcher-name api-path-matcher \
  --default-service app-servers \
  --path-rules "/v1/*=v1-backend, /v2/*=v2-backend" \
  --global

gcloud compute url-maps add-host-rule my-host-map \
  --host-rule-name images-rule \
  --hosts "images.example.com" \
  --path-matcher-name images-path-matcher \
  --global

gcloud compute url-maps add-path-matcher my-host-map \
  --path-matcher-name images-path-matcher \
  --default-service image-backend \
  --global

In this scenario, api.example.com would have its requests further routed based on /v1/* or /v2/* paths to different backends, while images.example.com would go directly to image-backend without further path inspection. The order of host rules matters if you have overlapping hostnames, but typically they are distinct.

The surprising part for many is how granular you can get with path matching. It’s not just simple prefixes. You can use exact path matching, and even regular expressions for complex routing logic. For example, to route requests for /users/123 and /users/456 but not /users/abc to a specific backend:

gcloud compute url-maps add-path-matcher my-app-url-map \
  --path-matcher-name user-specific-matcher \
  --default-service app-servers \
  --path-rules "/users/[0-9]+=user-specific-backend" \
  --global

This rule /users/[0-9]+ uses a regular expression to match paths starting with /users/ followed by one or more digits. The load balancer evaluates these rules in order. If you have multiple path matchers, the first one that contains a matching rule for the request’s host and path will be used.

When you configure these rules, you’re not just telling the load balancer where to send traffic; you’re defining the very boundaries of your application’s reach and behavior. The load balancer becomes a gatekeeper that can dynamically alter which service handles a request based on its URL, headers, and even the client’s IP address. This allows for sophisticated strategies like canary deployments, A/B testing, and fine-grained API versioning without touching your application code.

What most people miss is that the health check status of a backend service is evaluated per backend service and then applied to the rules. If image-servers becomes unhealthy, traffic matching /images/* will automatically be drained and routed to the default-service (or the next matching rule) if one exists. This failover behavior is automatic and a critical part of the load balancing system’s resilience.

The next step is often optimizing latency by configuring CDN policies on your URL map for static assets.

Want structured learning?

Take the full Load-balancing course →