Gmail is modifying image src URLs in HTML emails - how to fix this?

I’m building a booking system with Django and need to send HTML emails with header images when people sign up. The problem is Gmail keeps changing my image URLs which breaks the images.

My current code:

def handle_signup_form(request, booking_id, booking_title):
    booking = get_object_or_404(Booking, id=booking_id, title=booking_title)
    email_template = get_object_or_404(EmailTemplate, booking=booking)
    
    readonly_mode = 'preview' in request.path
    
    if request.method == 'POST' and not readonly_mode:
        form_data = {field.name: request.POST.get(f'input-{field.id}', '') for field in email_template.form_fields.all()}
        user_email = next((val for key, val in form_data.items() if '@' in val), None)
        
        # Create form submission record
        FormSubmission.objects.create(template=email_template, user_email=user_email, data=form_data)
        
        # Send notification email
        notification_settings = EmailConfig.objects.filter(booking=booking, type='signup_confirmation').first()
        if notification_settings and user_email:
            tunnel_url = 'https://abc123.ngrok-free.app'
            header_image_url = f"{tunnel_url}{notification_settings.header_image.url}"
            
            email_html = f"""
                <html>
                    <head>
                        <style>
                            .header-section img {{
                                max-width: 100%;
                                display: block;
                            }}
                            .footer-section {{
                                padding-top: 15px;
                                font-size: 12px;
                                color: #666;
                            }}
                        </style>
                    </head>
                    <body>
                        <div class="header-section">
                            <img src="{header_image_url}" alt="Header Image">
                        </div>
                        <div class="content-section">
                            {notification_settings.body_text}
                        </div>
                        <div class="footer-section">
                            {notification_settings.footer_text}
                        </div>
                    </body>
                </html>
            """
            
            send_mail(
                notification_settings.subject_line,
                notification_settings.body_text,
                '[email protected]',
                [user_email],
                fail_silently=False,
                html_message=email_html
            )
        
        return JsonResponse({'success': True, 'msg': 'Registration completed!'})
    
    # Handle GET request logic here...

What Gmail does to my URLs:
Original: https://abc123.ngrok-free.app/media/headers/my-image.jpg
Gmail changes it to: https://ci3.googleusercontent.com/meips/[long-hash]#https://abc123.ngrok-free.app/media/headers/my-image.jpg

This causes the images to not load properly. I need the original URL to stay unchanged so the images work correctly in Gmail. What’s the best way to handle images in HTML emails to prevent Gmail from doing this URL modification?

Gmail proxies images through their servers to block tracking pixels and malicious content - that’s why you’re seeing the URL change. The real problem is your ngrok tunnel probably isn’t playing nice with Gmail’s proxy servers.

I’ve hit this same issue with Django projects. Ngrok URLs often have auth requirements or rate limiting that mess with Gmail’s image proxy. Quick fix: add the ngrok-skip-browser-warning header to your image responses. Better fix: use a proper CDN like AWS S3 or Cloudinary for dev work.

For production, just make sure your image URLs are publicly accessible without auth. Gmail’s proxy handles standard HTTPS URLs just fine. Double-check your images have proper MIME types and aren’t too big - Gmail will reject oversized images.

Same issue here with Gmail’s image proxy in Django. Ngrok’s your problem - Gmail’s proxy servers time out on those temporary URLs super quick. For testing, just throw your images on Imgur or Dropbox. They handle Gmail’s crawlers way better than ngrok.

This isn’t a bug - it’s just how Gmail works. Gmail proxies all external images to protect users from tracking and malicious content. That googleusercontent URL means it’s working correctly - Gmail downloads your image and serves it through their proxy.

Your real problem is ngrok. Those tunnels aren’t reliable for email images since they can expire or have connection issues when Gmail tries to fetch them. I’ve hit this in several Django projects and found temporary cloud storage works way better for development.

Just upload your header images to a free AWS S3 bucket or GitHub Pages for testing. Gmail’s proxy handles regular public URLs fine. Make sure your images have proper headers and stay accessible. Once you deploy with a real domain, this usually fixes itself.