#1629·chromedp

chromedp code passes Cloudflare Turnstile locally but not Docker container

Author: romvaracCreated May 7, 2026Updated May 7, 2026

The same chromedp program, against the same Cloudflare-protected URL, works on my macOS host but fails consistently inside a
Docker container with the same Chromium binary version. The container never advances past the Cloudflare interstitial — the page title
stays "Just a moment..." for the entire 60-second wait, then the script gives up.

#### Source

  ```go
  // main.go
  package main                                                                                                                               
   
  import (                                                                                                                                   
        "context" 
        "fmt"
        "os"
        "strings"
        "time"

        "github.com/chromedp/chromedp"
  )                                                                                                                                          
   
  func main() {                                                                                                                              
        opts := append(chromedp.DefaultExecAllocatorOptions[:],
                chromedp.Flag("headless", false),                                                                                            
                chromedp.Flag("no-sandbox", true),
                chromedp.Flag("disable-dev-shm-usage", true),                                                                                
                chromedp.Flag("disable-blink-features", "AutomationControlled"),                                                             
        )
        if path := os.Getenv("CHROME_PATH"); path != "" {                                                                                    
                opts = append(opts, chromedp.ExecPath(path))
        }                                                                                                                                    
                  
        allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)                                                         
        defer cancel()                                                                                                                       
        ctx, cancel := chromedp.NewContext(allocCtx)
        defer cancel()                                                                                                                       
                                                                                                                                             
        if err := chromedp.Run(ctx, chromedp.Navigate("https://community.cloudflare.com")); err != nil {
                fmt.Fprintln(os.Stderr, "navigate:", err)                                                                                    
                os.Exit(1)                                                                                                                   
        }                                                                                                                                    
                                                                                                                                             
        deadline := time.Now().Add(60 * time.Second)
        start := time.Now()                                                                                                                  
        for time.Now().Before(deadline) {
                var title string                                                                                                             
                _ = chromedp.Run(ctx, chromedp.Title(&title))
                fmt.Printf("[%6.1fs] title=%q\n", time.Since(start).Seconds(), title)                                                        
                if title != "" && !strings.Contains(strings.ToLower(title), "just a moment") {                                               
                        fmt.Println("OK: cleared")                                                                                           
                        return                                                                                                               
                }                                                                                                                            
                time.Sleep(500 * time.Millisecond)                                                                                           
        }         
        fmt.Println("FAIL: blocked by Cloudflare (title never changed)")
        os.Exit(1)                                                                                                                           
  }

Local (macOS) — works

$ go run .
[   0.0s] title=""                                                                                                                         
[   0.5s] title="Just a moment..."
[   1.0s] title="Just a moment..."
[   1.5s] title="Just a moment..."                                                                                                         
[   2.0s] title="Just a moment..."
[   2.5s] title="Just a moment..."                                                                                                         
[   3.0s] title="Just a moment..."
[   3.5s] title="Just a moment..."                                                                                                         
[   4.0s] title="Just a moment..."
[   4.5s] title="Cloudflare Community"                                                                                                 
OK: cleared                                                                                                                                

Container — fails

Dockerfile:

dockerfile
# ---------- Builder ----------
FROM golang:1.26-bookworm AS builder
WORKDIR /src

COPY go.mod go.sum ./
RUN go mod download

COPY *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/crawler .

# ---------- Runtime ----------
FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
  ca-certificates \
  chromium \
  chromium-driver \
  xvfb \
  dumb-init \
  && rm -rf /var/lib/apt/lists/*

ENV DISPLAY=:99 \
  CHROME_PATH=/usr/bin/chromium

WORKDIR /app
COPY --from=builder /out/crawler /app/crawler

ENTRYPOINT ["dumb-init", "--"]
CMD ["sh", "-c", "Xvfb :99 -screen 0 1280x1024x24 -nolisten tcp & exec /app/crawler"]                                                        
$ docker build -t chromedp-repro . && docker run --rm chromedp-repro
[   0.0s] title=""
[   0.5s] title="Just a moment..."
[   1.0s] title="Just a moment..."
[   1.5s] title="Just a moment..."
... (120 polls, all "Just a moment...")
[  60.0s] title="Just a moment..."
FAIL: blocked by Cloudflare (title never changed)