Charles Proxy is an HTTP debugging proxy that captures and decrypts network traffic between your device and the internet. This guide covers installation, SSL configuration, advanced debugging techniques, and hidden tricks that most tutorials miss.
Whether you're debugging APIs, reverse engineering mobile apps, or testing network conditions, you'll find practical examples and ready-to-use scripts here.
What is Charles Proxy?
Charles Proxy works as a man-in-the-middle between your applications and remote servers. It intercepts HTTP and HTTPS traffic, letting you inspect requests, modify responses, and simulate various network conditions.
Unlike browser DevTools, Charles captures traffic from any application on your system. This includes mobile apps, desktop software, command-line tools, and background services.
The tool runs locally on your machine and creates a proxy server (default port 8888). Applications route traffic through this proxy, giving you complete visibility into the network layer.
Installing Charles Proxy
Head to the official Charles Proxy website and download the installer for your operating system. The tool supports macOS, Windows, and Linux.
macOS Installation:
# Using Homebrew
brew install --cask charles
# Or download directly from charlesproxy.com
Windows Installation:
Download the .msi installer and run it. Charles 5 now offers both .appx and .msi packages. The .appx version integrates better with Windows Store apps.
Linux Installation:
# Add Charles repository
wget -q -O - https://www.charlesproxy.com/packages/apt/PublicKey | sudo apt-key add -
sudo sh -c 'echo deb https://www.charlesproxy.com/packages/apt/ charles-proxy main > /etc/apt/sources.list.d/charles.list'
# Install
sudo apt-get update
sudo apt-get install charles-proxy
After installation, launch Charles. On first run, it asks permission to configure system proxy settings automatically. Grant this permission to start capturing traffic immediately.
Configuring SSL Proxying
HTTPS traffic appears encrypted by default. To inspect it, you need to enable SSL proxying and install the Charles root certificate.
Step 1: Enable SSL Proxying
Navigate to Proxy > SSL Proxying Settings. Check "Enable SSL Proxying" and add the domains you want to decrypt.
Use *:* to proxy all HTTPS traffic. For targeted debugging, add specific domains like api.example.com:443.
Step 2: Install Root Certificate
On macOS:
Help > SSL Proxying > Install Charles Root Certificate
This opens Keychain Access. Find the Charles Proxy CA certificate, double-click it, expand Trust, and set "When using this certificate" to "Always Trust".
On Windows:
Help > SSL Proxying > Install Charles Root Certificate
Follow the wizard to install the certificate in the Trusted Root Certification Authorities store.
On iOS devices:
- Connect your device to the same WiFi network
- Set the HTTP proxy to your computer's IP and port 8888
- Open Safari and navigate to
chls.pro/ssl - Download and install the profile
- Go to
Settings > General > About > Certificate Trust Settings - Enable full trust for Charles Proxy CA
On Android devices:
# For Android 7+, you need to add network security config
# Add to AndroidManifest.xml:
# android:networkSecurityConfig="@xml/network_security_config"
Create res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
For system-level certificate installation on Android emulators:
# Get certificate hash
openssl x509 -inform PEM -subject_hash_old -in charles-ssl-proxying-certificate.pem | head -1
# Output: 4fe145fd
# Rename certificate
mv charles-ssl-proxying-certificate.pem 4fe145fd.0
# Push to emulator (requires root)
adb root
adb remount
adb push 4fe145fd.0 /system/etc/security/cacerts/
adb shell chmod 644 /system/etc/security/cacerts/4fe145fd.0
adb reboot
Inspecting HTTP Traffic
Once SSL proxying is configured, Charles displays all network traffic in two views.
Structure View organizes requests by domain in a tree hierarchy. Expand any domain to see individual endpoints and resources.
Sequence View shows requests chronologically with timing information. This helps identify slow requests and understand request order.
Click any request to see details in the bottom panel:
- Overview: URL, method, status, timing
- Request: Headers, query parameters, body
- Response: Status, headers, body content
- Timing: DNS, connect, SSL handshake, request, response phases
For JSON responses, Charles automatically formats and syntax-highlights the content. You can search within responses using Cmd+F (Mac) or Ctrl+F (Windows).
Using Breakpoints for Live Debugging
Breakpoints pause requests or responses mid-flight. This lets you inspect and modify data before it reaches its destination.
Setting a Breakpoint:
Right-click any request and select Breakpoints. Charles pauses execution whenever a matching request occurs.
You can also configure breakpoints globally via Proxy > Breakpoint Settings. Add URL patterns and choose whether to break on request, response, or both.
Practical Example: Testing Authentication Edge Cases
Suppose you're debugging a login flow. Set a breakpoint on the login endpoint:
- Right-click the login request
- Select
Breakpoints - Attempt login in your app
- Charles pauses the request
- Modify the payload (e.g., change password)
- Click "Execute" to continue
You can also modify responses to test how your app handles different states:
// Original response
{
"user": "alice",
"role": "user"
}
// Modified response (test admin access)
{
"user": "alice",
"role": "admin"
}
This technique helps QA teams verify authorization logic without backend changes.
Rewrite Rules for Automated Modifications
Breakpoints require manual intervention. For automated modifications, use Rewrite rules.
Navigate to Tools > Rewrite and enable the feature. Create rule sets that automatically modify requests or responses based on patterns.
Common Rewrite Use Cases:
1. Redirect API Endpoints
Type: Host
Match: api.production.com
Replace: api.staging.com
2. Add Custom Headers
Type: Add Header
Match: Host = api.example.com
Header Name: X-Debug-Mode
Header Value: true
3. Modify Response Body
Type: Body
Match: URL = */api/config
Find: "feature_enabled": false
Replace: "feature_enabled": true
4. Change Status Codes
Type: Response Status
Match: URL = */api/user
Value: 500
This last example helps test error handling without waiting for actual server failures.
Map Local: Serve Files from Your Machine
Map Local overrides remote resources with local files. This is incredibly useful for frontend development.
Navigate to Tools > Map Local and add mappings.
Example: Override a JavaScript File
Remote URL: https://example.com/js/app.bundle.js
Local Path: /Users/you/project/dist/app.bundle.js
Now every request for the production bundle serves your local development version instead. You can test code changes against production data without deploying.
Example: Mock API Responses
Create a local JSON file:
// /Users/you/mocks/users.json
{
"users": [
{"id": 1, "name": "Test User", "email": "test@example.com"}
]
}
Map the API endpoint:
Remote URL: https://api.example.com/users
Local Path: /Users/you/mocks/users.json
Your app now receives mocked data. This speeds up frontend development when backend APIs aren't ready.
Map Remote: Redirect Traffic Between Environments
Map Remote redirects requests to different servers. Unlike Map Local, the response still comes from a server—just a different one.
Navigate to Tools > Map Remote.
Example: Test Against Staging
Map From: https://api.production.com/
Map To: https://api.staging.com/
All production API calls now hit staging. Your production-configured app tests against staging data.
Example: Route Specific Endpoints
Map From: https://api.example.com/v2/
Map To: https://api.example.com/v3/
Test v3 API compatibility without changing your app's configuration.
Network Throttling: Simulate Real-World Conditions
Most apps work fine on fast connections. Real-world users often have slower, unreliable networks.
Navigate to Proxy > Throttle Settings to configure bandwidth limits and latency.
Built-in Presets:
- 56 kbps Modem
- 256 kbps ISDN
- 3G
- 4G
- DSL
Custom Configuration:
For more control, set individual parameters:
Bandwidth: 256 kbps (download) / 128 kbps (upload)
Utilisation: 80%
Round-trip Latency: 300 ms
MTU: 1500 bytes
Reliability: 95%
Stability: 90%
The Reliability setting simulates packet loss. Set it to 90% to drop 10% of packets, revealing timeout handling issues.
Throttling Specific Hosts:
Check "Only for selected hosts" and add domains. This throttles specific APIs while keeping other traffic fast—useful when you need to download documentation while testing slow conditions.
Enable throttling with Proxy > Start Throttling or press Cmd+T (Mac) / Ctrl+T (Windows).
Reverse Engineering APIs with Charles Proxy
Modern web apps and mobile apps communicate via APIs that aren't publicly documented. Charles Proxy helps discover and understand these hidden endpoints.
Step 1: Capture Traffic
Open Charles and enable SSL proxying for the target domain. Use the app normally, triggering the features you want to understand.
Step 2: Analyze Requests
Filter traffic to the target domain. Look for patterns:
- Authentication headers (Bearer tokens, API keys)
- Session cookies
- Request payloads
- Pagination parameters
- GraphQL queries
Step 3: Export and Replicate
Right-click any request and select Copy cURL Request. This generates a command you can run in terminal:
curl 'https://api.example.com/data' \
-H 'Authorization: Bearer eyJ...' \
-H 'Content-Type: application/json' \
--data-raw '{"query":"products","limit":20}'
Use this curl command to test modifications. Once you understand the API, replicate it in your scraping code.
Python Example:
import requests
headers = {
'Authorization': 'Bearer eyJ...',
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)'
}
payload = {
'query': 'products',
'limit': 20,
'cursor': None
}
response = requests.post(
'https://api.example.com/data',
headers=headers,
json=payload
)
data = response.json()
print(data)
This approach bypasses complex JavaScript rendering. Direct API calls are faster and more reliable than headless browsers.
Mobile App Debugging
Charles Proxy excels at mobile app debugging. Both iOS and Android apps route traffic through your computer's proxy.
iOS Setup:
- Connect iPhone/iPad to same WiFi as your computer
- Go to
Settings > Wi-Fi > [Your Network] > Configure Proxy - Select "Manual"
- Enter your computer's IP (find it in
Help > Local IP Address) - Port: 8888
- Install SSL certificate from
chls.pro/ssl - Enable certificate trust in Settings
Android Setup:
- Connect device to same WiFi
- Long-press the network, select "Modify network"
- Show advanced options
- Set proxy to Manual
- Hostname: your computer's IP
- Port: 8888
- Install certificate (download from
chls.pro/ssl)
iOS Simulator:
Charles automatically installs certificates in iOS Simulators:
Help > SSL Proxying > Install Charles Root Certificate in iOS Simulators
Restart the simulator after installation.
Android Emulator:
Launch the emulator with proxy configuration:
emulator @Pixel_4_API_30 -http-proxy http://192.168.1.100:8888
Replace the IP with your actual machine IP.
Dealing with SSL Pinning
Some apps implement SSL pinning, which validates the server's certificate against a hardcoded value. This blocks Charles Proxy's man-in-the-middle approach.
Detecting SSL Pinning:
If Charles shows "SSL Handshake Failed" for an app while Safari works fine, the app likely uses SSL pinning.
Bypassing SSL Pinning (for apps you control or have permission to test):
Option 1: Disable pinning in debug builds
For iOS apps, remove SSL pinning code in debug configurations. For Android, use the network security config shown earlier.
Option 2: Use Frida (requires jailbreak/root)
Install Frida and run a pinning bypass script:
# Install Frida
pip install frida-tools
# Start the app with bypass
frida -U -f com.example.app -l ssl-bypass.js
Universal SSL bypass script for Android:
Java.perform(function() {
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.checkTrustedRecursive.implementation = function() {
console.log('[+] Bypassing SSL pinning');
return Java.use('java.util.ArrayList').$new();
};
});
Option 3: SSL Kill Switch (iOS jailbreak)
Install SSL Kill Switch 2 from Cydia to disable pinning system-wide.
Note: Only bypass SSL pinning on apps you own or have explicit permission to test.
Automation and Scripting
Charles supports JavaScript-based scripting for automated traffic manipulation.
Basic Script Structure:
function onRequest(event) {
var request = event.getRequest();
// Log all requests to a specific host
if (request.getHost() === 'api.example.com') {
event.console.log('Request: ' + request.getPath());
}
// Remove tracking headers
request.removeHeader('X-Tracking-ID');
}
function onResponse(event) {
var response = event.getResponse();
// Modify response headers
response.setHeader('X-Debug', 'true');
}
Enable scripting in Tools > Scripting and paste your script.
Practical Script: Remove Cache Headers
function onRequest(event) {
var request = event.getRequest();
request.removeHeader('If-Modified-Since');
request.removeHeader('If-None-Match');
event.console.log('Removed cache headers from: ' + request.getURL());
}
This forces fresh responses instead of 304 Not Modified.
Headless Mode and CLI Automation
Charles can run without a UI, useful for CI/CD pipelines and automated testing.
Start Charles Headless:
# macOS
/Applications/Charles.app/Contents/MacOS/Charles -headless
# Windows
"C:\Program Files\Charles\Charles.exe" -headless
# Linux
charles -headless
Control via Web Interface:
Enable the web interface in Proxy > Web Interface Settings. Access it at http://control.charles/.
Control Charles programmatically:
# Start recording
curl -x http://localhost:8888 http://control.charles/recording/start
# Stop recording
curl -x http://localhost:8888 http://control.charles/recording/stop
# Enable/disable throttling
curl -x http://localhost:8888 http://control.charles/throttling/enable
curl -x http://localhost:8888 http://control.charles/throttling/disable
# Enable/disable rewriting
curl -x http://localhost:8888 http://control.charles/tools/rewrite/enable
Export Session Data:
# Convert session to JSON
charles convert session.chls session.chlsj
# Convert session to HAR
charles convert session.chls session.har
JSON sessions are easier to parse and analyze programmatically.
Exporting and Sharing Sessions
Charles sessions contain all captured traffic. Export them for collaboration or offline analysis.
Save Session:
File > Save Session As creates a .chls file. Share this with teammates who can open it in their Charles installation.
Export as HAR:
File > Export Session > HAR creates a standard HTTP Archive file. HAR files open in Chrome DevTools or other analysis tools.
Export as Trace:
File > Export Session > Trace includes timing information useful for performance analysis.
Hidden Tricks and Power User Tips
1. Quick Filter by Type
Type json in the filter box to show only JSON requests. Works with any content type: html, css, js, image.
2. Focus Mode
Right-click a host and select "Focus". Charles grays out everything else, reducing visual noise.
3. Repeat Advanced
Right-click a request and select Repeat Advanced. Configure iterations, concurrency, and delays for simple load testing.
4. Compare Requests
Select two requests, right-click, and choose "Compare". Charles shows a diff of headers and body.
5. Find in Session
Edit > Find (Cmd+F) searches across all captured traffic—requests, responses, headers, and bodies.
6. Clear with Shortcut
Cmd+K (Mac) or Ctrl+K (Windows) clears the session. Faster than using menus.
7. Auto-Save Sessions
Tools > Auto Save automatically saves sessions at intervals. Never lose captured traffic to crashes.
8. DNS Spoofing
Tools > DNS Spoofing redirects domains to different IPs without changing your hosts file.
9. Mirror Tool
Tools > Mirror saves all responses to disk automatically. Useful for creating offline copies of websites.
10. External Proxy Chaining
Proxy > External Proxy Settings routes Charles traffic through another proxy. Chain with residential proxies for geo-testing.
Troubleshooting Common Issues
Traffic Not Appearing:
- Verify proxy settings point to 127.0.0.1:8888
- Check that macOS/Windows proxy is enabled in Charles
- Restart the application you're debugging
HTTPS Shows "Unknown" or Encrypted:
- Install and trust the Charles root certificate
- Add the domain to SSL Proxying Settings
- Restart browsers after certificate changes
Mobile Device Not Connecting:
- Confirm both devices are on same network
- Check firewall isn't blocking port 8888
- Verify IP address is correct (not localhost)
App Crashes or Fails:
- The app might use SSL pinning
- Try disabling SSL proxying for that domain
- Check for certificate trust issues
Charles Consumes Too Much Memory:
Proxy > Recording Settingslimits session size- Clear sessions periodically with Cmd+K
- Disable recording for domains you don't need
Conclusion
Charles Proxy remains one of the most powerful tools for HTTP debugging, API testing, and reverse engineering. Its combination of traffic inspection, modification capabilities, and mobile support makes it invaluable for developers and security researchers.
The techniques covered here—SSL proxying, breakpoints, rewriting, throttling, and automation—handle most debugging scenarios you'll encounter. For web scraping projects, Charles helps discover hidden APIs that are faster and more reliable than HTML parsing.
If you're doing large-scale data collection through proxies, consider pairing Charles with rotating residential proxies from providers like Roundproxies to avoid IP blocks during your research.
Start with basic traffic inspection, then gradually explore advanced features as your debugging needs grow. The investment in learning Charles pays dividends across mobile development, web development, and security testing.