GCP HTTP(S) Load Balancer Setup: Step-by-Step Guide
A Google Cloud HTTP(S) Application Load Balancer gives your web application a single global public IP, terminates HTTPS at Google’s edge, routes traffic to healthy backend VMs, and automatically redirects plain HTTP to HTTPS. This guide walks through the complete setup from scratch using gcloud, in the correct dependency order so you end up with a working load balancer rather than a pile of failed resource-not-found errors.
What is a GCP HTTP(S) load balancer?
The GCP HTTP(S) Application Load Balancer is a global, Layer 7 load balancer. “Layer 7” means it understands HTTP — it can inspect the URL path, route different paths to different backends, and terminate TLS so your backend VMs never have to handle certificates themselves.
”Global” means Google distributes the load balancer across its worldwide network using Anycast routing. A single IP address is announced from Google’s edge locations around the world. A user in Tokyo and a user in London connect to the same IP, but Google routes each to the nearest healthy backend automatically.
This is the right load balancer for public web applications and APIs served from Compute Engine VMs. It is different from the internal load balancer, which handles private traffic inside your VPC between services, and different from the external TCP/UDP load balancer, which operates at Layer 4 without HTTP-level routing.
How it works in plain English
Think of it like a smart reception desk at a large hotel. Every guest arrives at the same front entrance (the global IP). The receptionist checks their booking and verifies their identity if needed (TLS termination at the forwarding rule). A routing guide behind the desk says which department handles what kind of guest (the URL map). Before sending anyone through, the receptionist checks a board showing which staff members are actually on shift and available (the health check). Guests are never sent to a team member who called in sick that morning — they are routed around them automatically.
Here is what actually happens step by step when a request comes in:
- A user’s browser looks up your domain and gets the global static IP address you reserved in GCP.
- The browser connects to that IP on port 443 (HTTPS) or port 80 (HTTP).
- A forwarding rule receives the connection and hands it to the appropriate proxy.
- For HTTPS: the target HTTPS proxy terminates TLS using your SSL certificate. The encrypted connection ends here, at Google’s edge — not at your VM.
- The proxy consults the URL map to decide which backend should handle this request.
- The backend service checks which of its backends are currently healthy (using the health check results) and picks one.
- The request is forwarded from the load balancer to the backend VM on port 80, entirely over Google’s internal network.
- For HTTP on port 80: a separate forwarding rule catches it and returns a 301 redirect to HTTPS, before the request ever reaches your VMs.
The health check runs continuously in the background, probing each VM every few seconds. VMs that stop responding are pulled from rotation automatically and re-added once they recover.
What you are building
By the end of this guide you will have all of the following, wired together and working:
- Global static IP address. The permanent public IP your DNS record points at. Survives load balancer rebuilds.
- Managed instance group. The pool of backend VMs that serve your application traffic.
- Health check. The probe that decides which backends are healthy enough to receive requests.
- Backend service. The configuration layer that links the instance group and health check, and defines balancing behaviour.
- URL map. The routing rules that decide which backend service handles a given request path.
- Google-managed SSL certificate. Auto-provisioned and auto-renewed TLS for your domain.
- Target HTTPS proxy. The TLS termination point that binds the URL map and certificate together.
- HTTPS forwarding rule. Exposes port 443 on your global IP and routes traffic to the HTTPS proxy.
- HTTP redirect forwarding rule. Catches port 80 requests and returns a 301 to HTTPS.
When to use this setup
This is the right setup when you:
- Are serving a public web application or API from Compute Engine VMs
- Need a single public entry point with automatic HTTPS termination
- Want Google to handle SSL certificate provisioning and renewal
- Need health-based routing that automatically removes unhealthy VMs from rotation
- Want to distribute traffic across multiple VMs or across multiple zones
- May need path-based routing later, such as sending
/api/*to one backend and/static/*to another without changing infrastructure
This is not the right setup if:
- You only need to load-balance traffic inside your VPC. Use an internal load balancer instead.
- You are using Cloud Run or App Engine. Those services have built-in HTTPS and do not need this setup.
- You need raw TCP or UDP load balancing without HTTP routing. Use the external TCP/UDP load balancer.
- You want the simplest possible public HTTPS endpoint for a prototype. Cloud Run with a custom domain is faster to configure.
Component overview and build order
Each component depends on the ones before it. You cannot create a forwarding rule before its proxy exists, and you cannot create the proxy before its URL map exists. Attempting to skip ahead produces resource-not-found errors. Follow this order:
- Global static IP address. The public entry point. Everything else eventually references this IP.
- VM instance group. The actual backends. Must exist before the backend service can reference them.
- Health check. Defines how backends are probed. Must exist before the backend service is created.
- Backend service. Links instance groups and health checks. Must exist before the URL map references it.
- URL map. Defines routing rules. Must exist before the proxy references it.
- SSL certificate. Required before the target HTTPS proxy can be created.
- Target HTTPS proxy. Terminates TLS. References both the URL map and certificate. Must exist before the forwarding rule.
- Forwarding rule. The final piece. Binds the IP address and port to the proxy.
The firewall rule for health checks is technically independent of this chain, but create it alongside the health check. If you wait, there will be a window where backends exist but appear unhealthy because the probes are being blocked.
Step 1: Reserve a global IP address
Reserve the IP address first so you have it ready for DNS configuration. This IP is permanent — it persists even if you tear down and rebuild every other part of the load balancer, which means you do not need to update DNS records if you recreate the setup later.
gcloud compute addresses create web-lb-ip \
--global \
--ip-version=IPV4
# Get the assigned IP — you will need this for your DNS A record
gcloud compute addresses describe web-lb-ip \
--global \
--format="get(address)"Write down the IP address. The Google-managed SSL certificate you create later will not provision until your domain’s DNS A record points here. If you manage DNS in GCP, see Cloud DNS setup for how to create the A record.
Step 2: Create the backend instance group
The backend is a managed instance group (MIG), which is a pool of identical VMs that GCP maintains for you. The MIG automatically replaces any VM that fails health checks, and can scale up and down based on load. For production use, run at least two instances across zones for redundancy.
You first create an instance template that defines the VM configuration (machine type, OS, tags, startup script). Then you create the MIG from that template.
# Instance template — defines the VM configuration for every instance in the group
gcloud compute instance-templates create web-template \
--machine-type=e2-medium \
--image-family=debian-12 \
--image-project=debian-cloud \
--network=production-vpc \
--subnet=web-subnet \
--tags=web-server \
--metadata=startup-script='#!/bin/bash
apt-get update && apt-get install -y nginx
echo "Hello from $(hostname)" > /var/www/html/index.html
systemctl start nginx'
# Create the managed instance group from the template
gcloud compute instance-groups managed create web-mig \
--template=web-template \
--size=2 \
--zone=us-central1-a
# Set named ports so the load balancer knows which port serves HTTP traffic
gcloud compute instance-groups set-named-ports web-mig \
--named-ports=http:80 \
--zone=us-central1-aThe set-named-ports step is easy to skip but will break everything downstream. The backend service references backends by port name (like http), not by number. If the instance group has no named port configured, the backend service cannot resolve where to send traffic and all backends appear unhealthy regardless of your firewall rules.
If you want the group to scale automatically with traffic, read autoscaling instance groups after completing this setup.
Step 3: Create a health check and firewall rule
The health check defines how the load balancer probes your backends to decide which ones are ready to receive traffic. GCP’s health checker infrastructure sends HTTP requests to each VM at the defined interval.
gcloud compute health-checks create http hc-http-80 \
--port=80 \
--request-path=/ \
--check-interval=10s \
--timeout=5s \
--healthy-threshold=2 \
--unhealthy-threshold=3This probes port 80 every 10 seconds. A VM is considered healthy after 2 consecutive successful responses (2xx or 3xx HTTP status) and marked unhealthy after 3 consecutive failures. Once unhealthy, a VM is removed from rotation and re-added after 2 successful probes.
Create the firewall rule below before you create the backend service. GCP’s health checkers always originate from 35.191.0.0/16 and 130.211.0.0/22. If your VPC firewall blocks these ranges, every backend will show as UNHEALTHY — even if the VMs are running and responding perfectly. This is by far the most common reason backends appear unhealthy after a fresh setup.
gcloud compute firewall-rules create allow-lb-health-checks \
--network=production-vpc \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:80 \
--source-ranges=35.191.0.0/16,130.211.0.0/22 \
--target-tags=web-serverThe —target-tags=web-server restricts this rule to VMs carrying that network tag, which your instance template assigns via —tags=web-server. See firewall rules explained for how network tags and ingress rules interact.
Step 4: Create the backend service
The backend service is the central coordination object. It links your instance group to the health check, defines how traffic is distributed, and is what the URL map references when deciding where to send a request.
# Create the backend service
gcloud compute backend-services create web-backend-service \
--protocol=HTTP \
--port-name=http \
--health-checks=hc-http-80 \
--global
# Add the instance group as a backend
gcloud compute backend-services add-backend web-backend-service \
--instance-group=web-mig \
--instance-group-zone=us-central1-a \
--balancing-mode=UTILISATION \
--max-utilization=0.8 \
--global—protocol=HTTP means the load balancer communicates with backends using plain HTTP. This traffic travels entirely inside Google’s network between the load balancer and your VMs, so it is not exposed to the public internet even though it is unencrypted. If you need end-to-end encryption all the way to the VM, use —protocol=HTTPS — but your VMs must also be configured to serve HTTPS.
—port-name=http must exactly match the named port you set on the instance group in Step 2. This is how the backend service resolves port 80 from the name http.
Step 5: Create the URL map
The URL map is the routing configuration for your load balancer. It inspects incoming requests and decides which backend service should handle them. For a single-backend setup, you just define a default service that catches all requests:
gcloud compute url-maps create web-url-map \
--default-service=web-backend-serviceThis routes all requests to web-backend-service regardless of path. If you later want path-based routing — sending /api/* to a different backend or serving /static/* from Cloud Storage — you update the URL map with additional path matcher rules without touching any other component in the load balancer.
Step 6: Create the SSL certificate and HTTPS proxy
The target HTTPS proxy is where TLS termination happens. It needs an SSL certificate before it can be created. A Google-managed certificate is the easiest option — GCP handles provisioning and renewal automatically:
# Google-managed certificate — provisioned and renewed automatically by GCP
gcloud compute ssl-certificates create web-ssl-cert \
--domains=example.com,www.example.com \
--global
# Target HTTPS proxy — binds the URL map and certificate together
gcloud compute target-https-proxies create web-https-proxy \
--url-map=web-url-map \
--ssl-certificates=web-ssl-cert \
--globalAfter creation, the certificate will be stuck in PROVISIONING state until your DNS A record points at the load balancer’s global IP. Google validates your domain by sending an HTTP-01 probe to port 80 at your domain name — if the domain does not resolve to the right IP yet, the probe hits the wrong place and validation fails silently. Set your DNS A record as soon as you have the IP from Step 1 and allow up to 30 minutes for the certificate to become ACTIVE after DNS propagation.
If you need more control — for example, using an existing certificate from a third-party CA, or managing certificates centrally across multiple load balancers — read SSL certificates in GCP for the alternatives.
Step 7: Create forwarding rules
Forwarding rules are the final piece. They bind a global IP address and port to a target proxy. You need two: one for HTTPS on port 443, and a second for HTTP on port 80 that redirects all traffic to HTTPS. Both use the same global IP.
# HTTPS forwarding rule — the main entry point for all HTTPS traffic
gcloud compute forwarding-rules create web-https-forwarding-rule \
--address=web-lb-ip \
--global \
--target-https-proxy=web-https-proxy \
--ports=443
# Create a redirect URL map that returns 301 HTTPS redirects for all HTTP requests
gcloud compute url-maps import http-redirect-map \
--global \
--source /dev/stdin << 'EOF'
defaultUrlRedirect:
redirectResponseCode: MOVED_PERMANENTLY_DEFAULT
httpsRedirect: true
EOF
# Target HTTP proxy for the redirect — uses a plain HTTP proxy, not HTTPS
gcloud compute target-http-proxies create web-http-proxy \
--url-map=http-redirect-map
# HTTP forwarding rule on port 80 — redirects all plain HTTP to HTTPS
gcloud compute forwarding-rules create web-http-forwarding-rule \
--address=web-lb-ip \
--global \
--target-http-proxy=web-http-proxy \
--ports=80With both rules in place, a visitor who types your domain without https:// hits the port 80 rule, receives a 301 redirect, and their browser reconnects automatically on port 443 via the main rule. Without the port 80 rule, plain HTTP requests would get a connection refused error. Browsers with a cached HSTS entry would be fine, but anyone without one would not reach your site.
The port 80 forwarding rule also serves a second purpose: it is what Google uses for HTTP-01 domain validation when provisioning your SSL certificate. Even if you plan to enforce HTTPS everywhere, the port 80 rule needs to exist for the certificate to provision successfully.
Verifying the setup
Run these checks once all components are created. Do not skip to DNS until backends are showing healthy.
# Backend health — wait until all instances show HEALTHY
gcloud compute backend-services get-health web-backend-service \
--global
# Forwarding rules — confirm both port 80 and port 443 rules exist
gcloud compute forwarding-rules list --global
# SSL certificate state — should move from PROVISIONING to ACTIVE after DNS is set
gcloud compute ssl-certificates describe web-ssl-cert \
--global \
--format="get(managed.status, managed.domainStatus)"
# Confirm the global IP is reserved and in use
gcloud compute addresses describe web-lb-ip \
--global \
--format="get(address, status)"What to look for:
- Backend health. Each instance should show
HEALTHY. If any showUNHEALTHY, the health check firewall rule is the first thing to check. See load balancer backend unhealthy for a full diagnostic walkthrough. - Forwarding rules. You should see two rules — one on port 443 pointing at the HTTPS proxy, one on port 80 pointing at the HTTP proxy.
- Certificate state.
PROVISIONINGis normal before DNS is pointed at the load balancer IP. It moves toACTIVEafter GCP validates the domain. If it stays stuck, check that the domain resolves correctly and that the port 80 forwarding rule exists. - HTTP redirect. Once live, test with
curl -I http://yourdomain.comand confirm a 301 response with aLocationheader pointing tohttps://. - HTTPS. Test with
curl -I https://yourdomain.comonce the certificate is active and confirm a 200 response from your backend.
If you need to debug connectivity at a lower level — VPC routes, firewall logs, packet loss — see troubleshooting network issues in GCP.
HTTP(S) load balancer vs internal load balancer
The two load balancers you are most likely to reach for are the external HTTP(S) Application Load Balancer (what this page covers) and the internal load balancer. They serve fundamentally different purposes:
| HTTP(S) Application LB (external) | Internal load balancer | |
|---|---|---|
| Traffic source | Public internet | Inside your VPC only |
| IP address type | Global static public IP | Private RFC 1918 IP |
| Layer | Layer 7 (HTTP-aware routing) | Layer 4 (TCP/UDP) or Layer 7 (regional) |
| SSL termination | Yes, at Google’s edge | Not on L4; optional on internal L7 |
| Scope | Global | Regional |
| Typical use | Public web apps, public APIs | Microservices, internal APIs, DB proxies |
If the service you are load-balancing only needs to be reachable from other services inside your VPC — for example, an internal API called by other VMs — the internal load balancer is simpler, cheaper, and keeps all traffic private. Use the external HTTP(S) load balancer when you need to accept traffic from the public internet.
For a full comparison of all GCP load balancer types, see external load balancers and global load balancing.
Common mistakes
- Creating resources out of order. The dependency chain is strict: instance group, health check, backend service, URL map, certificate, proxy, forwarding rule. Attempting to create the proxy before the URL map, or the forwarding rule before the proxy, will fail with a resource-not-found error.
- Forgetting to set named ports on the instance group. The backend service references backends by port name (like
http), not by port number. The instance group must have a named port configured withset-named-portsbefore the backend service can resolve where to send traffic. Without it, all backends appear unhealthy regardless of firewall rules. - Missing the health check firewall rule. GCP health checkers always originate from
35.191.0.0/16and130.211.0.0/22. If your VPC firewall does not allow ingress from those ranges on the backend port, every backend shows asUNHEALTHYeven if the VM is running normally. - Expecting the certificate to provision before DNS is set. Google-managed certificates validate via HTTP-01. GCP sends a probe to your domain on port 80. If your DNS A record does not yet point at the load balancer IP, the probe cannot reach the right place and the certificate stays in
PROVISIONINGindefinitely. - Skipping the HTTP-to-HTTPS redirect. If you only create the port 443 forwarding rule, visitors who navigate to your domain over HTTP get a connection refused error. Always create both forwarding rules on the same IP.
- Using a regional IP instead of a global IP. Global Application Load Balancers require a global IP address created with the
—globalflag. A regional IP will not attach and produces an error. Verify the address type withgcloud compute addresses describe web-lb-ip —global. - Assuming backend health means the application is working end-to-end. Backends show as
HEALTHYwhen the health check probe gets a 2xx or 3xx response. This only confirms the probe endpoint is responding — it does not mean every feature of your application works correctly. Always run a real end-to-end test after verifying backend health.
Summary
You have built a complete GCP HTTP(S) Application Load Balancer: a global static IP, a managed instance group of backend VMs, a health check with the correct firewall allowances, a backend service linking the two, a URL map for routing, a Google-managed SSL certificate, a target HTTPS proxy, and forwarding rules for both HTTPS traffic and HTTP redirect.
- Build in order: instance group, health check, backend service, URL map, certificate, proxy, forwarding rule
- Set named ports on the instance group to match the backend service’s
—port-name - Always allow health checker ranges
35.191.0.0/16and130.211.0.0/22in your firewall - DNS must point at the load balancer IP before Google-managed certificate provisioning can complete
- Create forwarding rules on both port 443 and port 80 — never leave port 80 unanswered
- Verify backend health, certificate state, and both forwarding rules before testing end-to-end
If your backends are not coming up healthy, check load balancer backend unhealthy for a diagnostic walkthrough. If you need to go beyond Google-managed certificates, read SSL certificates in GCP.
Frequently asked questions
What components do I need for a Google Cloud HTTP(S) load balancer?
You need eight components built in order: a global static IP address, a managed instance group (the backend VMs), a health check, a backend service linking the health check to the instance group, a URL map defining routing rules, a Google-managed SSL certificate, a target HTTPS proxy referencing the URL map and certificate, and a forwarding rule on port 443. For HTTP redirect, you also need a redirect URL map, a target HTTP proxy, and a second forwarding rule on port 80.
What is a URL map in GCP load balancing?
A URL map is the routing layer of an Application Load Balancer. It defines rules that match incoming requests by hostname or path and direct them to a backend service. A simple URL map sends all traffic to one backend. A more advanced one can route /api/* to an API backend, /static/* to Cloud Storage, and everything else to a default backend service.
Why are my backends showing as unhealthy?
The most common cause is a missing firewall rule. GCP health checkers probe backends from source ranges 35.191.0.0/16 and 130.211.0.0/22. If your firewall blocks ingress from those ranges on the service port, all backends appear unhealthy. The second most common cause is a missing or mismatched named port: the backend service references a port by name (like http), and the instance group must have a named port with exactly that name configured.
How do I redirect HTTP to HTTPS in GCP?
Create a separate redirect URL map with a defaultUrlRedirect rule that sets httpsRedirect: true. Attach it to a target HTTP proxy, then create a forwarding rule on port 80 pointing at that proxy using the same global IP address as your HTTPS forwarding rule. HTTP requests get a 301 redirect and the browser reconnects on port 443.
How long does a Google-managed SSL certificate take to provision?
Google-managed certificates use HTTP-01 domain validation. GCP sends a verification probe to your domain on port 80. The certificate stays in PROVISIONING state until your DNS A record points at the load balancer IP and DNS has propagated. Once DNS is correct, provisioning typically completes within 15 to 30 minutes. If it stays stuck, check that the domain resolves to the right IP and that port 80 has a forwarding rule responding.