- Created detailed best practices guide for Godot HTML5/Web exports - Removed empty stub files (they mask problems rather than solving them) - Kept favicon.ico as it's a legitimate browser requirement - Guide covers export config, optimization, deployment, and troubleshooting
11 KiB
Godot HTML5 Export Best Practices Guide
A comprehensive guide for exporting Godot games to HTML5/Web and deploying them properly.
Table of Contents
- Pre-Export Checklist
- Export Configuration
- Post-Export Optimization
- Deployment Best Practices
- Common Issues & Solutions
- Security Considerations
Pre-Export Checklist
1. Set Project Icon
Location: Project → Project Settings → Application → Config → Icon
✅ Use a square PNG image (ideally 512x512 or 1024x1024)
✅ Godot will auto-generate smaller sizes (16x16, 32x32, etc.)
✅ This icon becomes your favicon and app icons
Why: Prevents 404 errors for /favicon.ico and provides proper branding.
2. Configure Export Presets
Location: Project → Export → Add → HTML5
Essential Settings:
Export Type: Regular (or Threads if you need SharedArrayBuffer)
Export With Debug: OFF for production
Texture Format: Compress for Smaller Downloads
Head Include (Optional):
Only add custom meta tags if needed:
<meta name="description" content="Your game description">
<meta name="keywords" content="game, webgl, godot">
<meta name="author" content="Your Name">
3. Audio Configuration
Location: Project → Project Settings → Audio
✅ Mix Rate: 44100 Hz (standard web audio)
✅ Output Latency: 15ms (balance between quality and performance)
✅ Enable Audio Input: Only if needed (requires user permissions)
4. Display Settings
Location: Project → Project Settings → Display → Window
✅ Size: Set your target resolution (e.g., 1920x1080)
✅ Resizable: Enable if you want responsive sizing
✅ Stretch Mode: "2d" or "viewport" for web games
✅ Stretch Aspect: "expand" or "keep" based on your needs
Export Configuration
Recommended HTML5 Export Settings
1. Vram Texture Compression
Enable both formats for maximum compatibility:
✅ For Desktop: s3tc (DXT1/DXT5)
✅ For Mobile: etc2 (or astc if targeting newer devices)
2. Custom HTML Shell
Leave default unless you need:
- Custom loading screens
- Analytics integration
- Special PWA features
3. Export Mode
- Release: Production builds (optimized, no debug symbols)
- Debug: Development builds (larger, has debug info)
4. Progressive Web App (PWA)
Enable if you want installable web apps:
Export → HTML5 → Progressive Web App: ON
Offline Page: Enable for offline support
Icon Sizes: Godot auto-generates these
Post-Export Optimization
1. File Size Optimization
Compress WASM Files
The .wasm file is usually the largest. Use Brotli or Gzip:
# Using Brotli (best compression)
brotli -9 YourGame.wasm
# Using Gzip (better compatibility)
gzip -9 YourGame.wasm
Nginx Configuration:
# Enable compression for Godot files
gzip on;
gzip_types application/wasm application/javascript text/html;
gzip_min_length 1000;
# Better: Use Brotli
brotli on;
brotli_types application/wasm application/javascript text/html;
2. Caching Strategy
# Cache static assets aggressively
location ~* \.(wasm|js|pck)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Cache images
location ~* \.(png|jpg|jpeg|gif|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Don't cache the HTML file
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
3. Required HTTP Headers
For SharedArrayBuffer/Threads support:
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
Note: These are already in your nginx.conf - good job!
Deployment Best Practices
1. Directory Structure
your-game/
├── index.html # Main entry (or YourGame.html)
├── YourGame.js # Godot engine
├── YourGame.wasm # Compiled game
├── YourGame.pck # Game assets
├── YourGame.worker.js # Service worker
├── YourGame.icon.png # Favicon/Icon
├── favicon.ico # Browser favicon (convert from icon)
├── manifest.json # PWA manifest
└── assets/ # Optional: separate assets folder
2. Serving Files
Option A: Simple HTTP Server (Development)
# Python 3
python3 -m http.server 8000
# Python 2
python -m SimpleHTTPServer 8000
# Node.js (http-server)
npx http-server -p 8000
Option B: Production (Nginx/Caddy)
Use your current Kubernetes deployment approach.
3. HTTPS Requirement
Modern browsers require HTTPS for:
- SharedArrayBuffer (threads)
- Service Workers (offline)
- WebGL 2.0 features
- Clipboard API
- Gamepad API
Your setup already has this via cert-manager + Let's Encrypt ✅
Common Issues & Solutions
Issue 1: "SharedArrayBuffer is not defined"
Cause: Missing CORS headers or HTTP (not HTTPS)
Solution:
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
Issue 2: Blank Screen or "Failed to load"
Debug Steps:
- Open browser DevTools (F12)
- Check Console for errors
- Check Network tab for failed requests
Common Causes:
- Missing files (check all exports completed)
- Wrong MIME types
- File path mismatches (case-sensitive on Linux)
Solution: Verify nginx MIME types:
types {
application/wasm wasm;
application/javascript js;
text/html html;
}
Issue 3: Audio Not Playing
Cause: Browsers require user interaction before playing audio
Solution: In Godot, show a "Click to Start" screen:
func _ready():
# Don't autoplay audio
# Wait for user input first
pass
func _on_start_button_pressed():
$AudioStreamPlayer.play()
Issue 4: Poor Performance
Optimization Tips:
- Reduce Draw Calls: Batch sprites, use TileMaps
- Optimize Shaders: Simple shaders = better performance
- Texture Compression: Use compressed textures
- Object Pooling: Reuse nodes instead of creating/destroying
- Profile: Use Godot's built-in profiler
Issue 5: Large File Sizes
Solutions:
- Enable Compression: Gzip/Brotli on server
- Optimize Assets:
- Convert PNGs to WebP
- Compress audio (OGG Vorbis instead of WAV)
- Use texture compression
- Lazy Loading: Load assets on-demand, not all at startup
- Asset Streaming: Use Godot's asset streaming features
Security Considerations
1. Content Security Policy (CSP)
If you need CSP headers:
# Relaxed CSP for Godot games
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline'; worker-src 'self' blob:; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';" always;
2. X-Frame-Options
Prevent clickjacking:
add_header X-Frame-Options "SAMEORIGIN" always;
Your nginx.conf already has this ✅
3. Input Validation
If your game has:
- User-generated content
- Multiplayer features
- External data loading
Always validate and sanitize inputs!
Testing Checklist
Before deploying to production:
Browser Compatibility
- Chrome/Chromium (Latest)
- Firefox (Latest)
- Safari (Latest)
- Edge (Latest)
- Mobile browsers (iOS Safari, Chrome Mobile)
Performance Testing
- Load time < 10 seconds on 3G
- 60 FPS on target hardware
- Memory usage stable over time
- No memory leaks after extended play
Functionality Testing
- Audio works (after user interaction)
- Input (keyboard, mouse, touch, gamepad)
- Fullscreen mode works
- Save/Load functionality
- All game mechanics work as expected
Accessibility
- Works without sound (visual feedback)
- Keyboard-only controls (if applicable)
- Colorblind-friendly (if applicable)
Advanced: Custom Docker Build
For building in CI/CD (like your Kaniko setup):
Dockerfile Example:
FROM nginx:alpine
# Copy game files
COPY htlm/ /usr/share/nginx/html/
# Copy custom nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Set proper permissions
RUN chown -R nginx:nginx /usr/share/nginx/html
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1
nginx.conf (Your current one is excellent):
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
index YourGame.html;
# Compression
gzip on;
gzip_types application/wasm application/javascript text/html;
# CORS for SharedArrayBuffer
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Caching
location ~* \.(wasm|js|pck)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Resources
Official Documentation
Tools
- Godot Web Export Validator
- WebPageTest - Performance testing
- Lighthouse - PWA audit
Community
Your Current Setup Analysis
What You're Doing Right ✅
- Proper CORS Headers for SharedArrayBuffer
- Security Headers (X-Frame-Options, X-Content-Type-Options)
- Compression enabled (gzip)
- Proper MIME types for .wasm files
- Cache headers for static assets
- Service Worker for offline support
- GitOps deployment with Flux
- Container orchestration with Kubernetes
Recommendations
- ✅ Keep the favicon.ico (converted from your icon)
- ❌ Don't create empty stub files for scanner requests
- ✅ Monitor logs but ignore 404s for files your app doesn't use
- ✅ Consider adding a robots.txt to reduce scanner noise
- ✅ Consider adding rate limiting to prevent abuse
About Those 404 Errors
The files being requested (support_parent.css, twint_ch.js, lkk_ch.js) are:
- Not part of Godot exports
- Not needed by your game
- Requested by bots/scanners looking for e-commerce vulnerabilities
- Safe to ignore - they don't affect your game
Best Practice: Monitor real errors, ignore scanner noise in logs.
License
This guide is provided as-is for educational purposes. Feel free to adapt for your own projects.
Last Updated: November 2025 Godot Version: 4.x (applicable to 3.x with minor changes)