Posts and how to...
Many useful articles and instructions!
- Purpose
- Requirements
- Installation
- Intercepting App Headers (Debug)
- Script Configuration
- File Security (.htaccess)
- Testing
- Connecting to VPN Clients
- Bypassing Device Limits
- Additional Converters
- Troubleshooting
- Security
- File Structure
- PHP 7.4+ with support for extensions:
curlmbstringjson
- Web Server: Apache (recommended) or Nginx
- Write permissions in the script directory (for
output.cache) - (Optional)
mod_rewritemodule for.htaccess
How to extract the VLess server list from a HAPP subscription for vpn V2Ray and V2RayN apps, and connect devices that exceed the subscription limit
Instruction: Happ Proxy Subscription Converter
Parsing Happ Proxy Subscriptions for V2RayN / V2RayA / Clash / SingBox
⚠️ Important: This script is intended for educational purposes. Use only with a legal subscription and in accordance with your provider's terms of service.
Table of Contents
Purpose
The happ_converter.php script solves the following tasks:
| Task | Solution |
|---|---|
| Mobile App Emulation | Substitution of headers like User-Agent: Happ/3.13.0 |
| Bypass Device Verification | Using custom X-Hwid, X-Real-Ip |
| Subscription Decoding | Automatic Base64 decoding → list of vless:// |
| ⚡ Caching | Saving results for 3 hours to speed up loading |
| Universality | Works with V2RayN, V2RayA, Clash, SingBox |
⚙️ Requirements
Check cURL availability in PHP:
php -m | grep curl # or create a file info.php with <?php phpinfo();
Installation
Step 1: Create the file happ_converter.php
Copy the code below into a new file:
<?php
/**
* Happ Proxy Subscription Converter (Lite + Cache)
* For V2RayN / V2RayA
* Version: 1.2
*/
// ================= SETTINGS =================
$subscriptionUrl = trim('https://subscription.web.tech/YOUR_LINK');
// Headers of the Happ mobile application
$headers = [
'User-Agent: Happ/3.13.0',
'X-Device-Os: Android',
'X-Device-Locale: ru',
'X-Device-Model: ELP-NX1', // ← Replace with your model
'X-Ver-Os: 15', // ← Replace with your Android version
'Accept-Encoding: gzip',
'Connection: close',
'X-Hwid: 74jf74nf8f4jr5je', // ← Unique Device ID
'X-Real-Ip: 101.202.303.404',
'X-Forwarded-For: 101.202.303.404',
];
$timeout = 30; // Request timeout (seconds)
$cache_ttl = 10800; // Cache lifetime: 3 hours (in seconds)
$cache_file = __DIR__ . '/output.cache';
// =============================================
// ================= CACHE CHECK =================
if (file_exists($cache_file)) {
if ((time() - filemtime($cache_file)) < $cache_ttl) {
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-cache, max-age=0');
header('X-Cache: HIT');
echo file_get_contents($cache_file);
exit;
}
}
// ================= REQUEST TO HAPP SERVER =================
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $subscriptionUrl,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_ENCODING => '', // Auto-handle gzip
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
// Error handling
if ($error || $httpCode !== 200 || !$response) {
// Fallback: if there is an old cache — serve it
if (file_exists($cache_file)) {
header('Content-Type: text/plain; charset=utf-8');
header('X-Cache: STALE');
echo file_get_contents($cache_file);
exit;
}
http_response_code(502);
die("Error: " . ($error ?: "HTTP $httpCode"));
}
// Decode Base64 (if needed)
$decoded = base64_decode($response, true);
$output = $decoded ?: $response;
// Save to cache
file_put_contents($cache_file, $output, LOCK_EX);
// Serve result
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-cache, max-age=0');
header('X-Cache: MISS');
echo $output;
Intercepting App Headers (Debug)
Why is this needed?
If the provider updated the app and your headers are outdated, or if you want to use your own unique parameters (HWID, device model) — you need to intercept the actual headers sent by the Happ app.
Step 1: Create the file debug_headers.php
This script will save all incoming headers to a file requests.log:
<?php
/**
* Debug: Logging incoming requests from Happ App
* Saves all HTTP headers to requests.log
*/
// Path to log file
define('LOG_OUTPUT', __DIR__ . '/requests.log');
// Format data for writing
$data = sprintf(
"[%s]\n%s %s %s\n\nHTTP HEADERS:\n",
date('c'),
$_SERVER['REQUEST_METHOD'],
$_SERVER['REQUEST_URI'],
$_SERVER['SERVER_PROTOCOL']
);
// Collect all HTTP headers
foreach ($_SERVER as $name => $value) {
if (preg_match('/^HTTP_/',$name)) {
// Convert HTTP_HEADER_NAME to Header-Name
$name = strtr(substr($name,5),'_',' ');
$name = ucwords(strtolower($name));
$name = strtr($name,' ','-');
// Add to list
$data .= $name . ': ' . $value . "\n";
}
}
$data .= "\nREQUEST BODY:\n" . file_get_contents('php://input') . "\n";
// Write to file (with locking)
file_put_contents(LOG_OUTPUT, $data, FILE_APPEND|LOCK_EX);
echo("OK!\n");
Step 2: Configure the Happ App
- Open the Happ Proxy app settings
- Find the Subscription or Subscription URL section
- Enter the link to your server:
https://your-site.ru/debug_headers.php - Click Update Subscription or Sync
Step 3: Check the file requests.log
After updating the subscription in the app, the file requests.log will contain something like:
[2026-03-15T10:30:45+00:00]
GET /debug_headers.php HTTP/1.1
HTTP HEADERS:
Host: your-site.ru
User-Agent: Happ/3.13.0
X-Device-Os: Android
X-Real-Ip: 101.202.303.404
X-Device-Locale: ru
X-Device-Model: ELP-NX1
Connection: close
X-Hwid: 74jf74nf8f4jr5je
X-Ver-Os: 15
X-Forwarded-For: 101.202.303.404
Accept-Encoding: gzip
REQUEST BODY:
Step 4: Copy headers to happ_converter.php
Take the values from requests.log and paste them into the main script:
$headers = [
'User-Agent: Happ/3.13.0', // ← from log
'X-Device-Os: Android', // ← from log
'X-Device-Locale: ru', // ← from log
'X-Device-Model: ELP-NX1', // ← your model
'X-Ver-Os: 15', // ← your Android version
'Accept-Encoding: gzip',
'Connection: close',
'X-Hwid: 74jf74nf8f4jr5je', // ← your HWID
'X-Real-Ip: 101.202.303.404', // ← can keep or generate
'X-Forwarded-For: 101.202.303.404', // ← must match X-Real-Ip
];
Important: Delete the debug script after use!
# After copying the headers:
rm debug_headers.php
rm requests.log # or move to a safe location
⚠️ Do not leave
debug_headers.phpon the server!
It can be used to obtain information about your requests and headers.
⚙️ Script Configuration
Step 1: Replace the subscription link
// Before:
$subscriptionUrl = trim('https://subscription.web.tech/YOUR_LINK');
// After (your real link from happ://add/...):
$subscriptionUrl = trim('https://subscription.web.tech/qqwweeerrrttt45');
If your link is in the format
happ://add/https://..., remove the prefixhapp://add/.
Step 2: Configure headers
Use data from requests.log (see section above) or leave defaults:
$headers = [
'User-Agent: Happ/3.13.0',
'X-Device-Os: Android',
'X-Device-Locale: ru',
'X-Device-Model: ELP-NX1', // your phone model
'X-Ver-Os: 15', // your Android version
'Accept-Encoding: gzip',
'Connection: close',
'X-Hwid: 74jf74nf8f4jr5je', // unique identifier
'X-Real-Ip: 101.202.303.404',
'X-Forwarded-For: 101.202.303.404',
];
Step 3: Configure caching
$cache_ttl = 10800; // 3 hours in seconds
// Can be changed:
// 1 hour = 3600
// 6 hours = 21600
// 12 hours = 43200
File Security (.htaccess)
To protect cache files, create .htaccess in the same folder:
# Deny direct access to service files
<FilesMatch "\.(cache|meta|log|tmp)$">
Require all denied
</FilesMatch>
# Hide all files starting with a dot
<Files ~ "^\.">
Require all denied
</Files>
<Files ".htaccess">
Require all granted
</Files>
# Disable directory listing
Options -Indexes
# (Optional) Protect the script itself from direct access without parameters
<Files "happ_converter.php">
<If "%{QUERY_STRING} == ''">
Require all denied
</If>
</Files>
⚠️ For Apache 2.2 replace
Require all deniedwithDeny from all.
Testing
Test via terminal (curl)
# 1️⃣ First request (cache created, X-Cache: MISS)
curl -i "https://your-site.ru/happ_converter.php" | head -20
# Expected response:
# HTTP/1.1 200 OK
# Content-Type: text/plain; charset=utf-8
# X-Cache: MISS
# vless://uuid@ip:port?...
# vless://uuid@ip:port?...
# 2️⃣ Repeat request (cache used, X-Cache: HIT)
curl -i "https://your-site.ru/happ_converter.php" | grep "X-Cache"
# Response: X-Cache: HIT
# 3️⃣ Check .htaccess protection
curl -I "https://your-site.ru/output.cache"
# Response: HTTP/1.1 403 Forbidden
# 4️⃣ Check requests.log protection
curl -I "https://your-site.ru/requests.log"
# Response: HTTP/1.1 403 Forbidden
Test via browser
- Open:
https://your-site.ru/happ_converter.php - You should see a list of links like:
vless://343e5f1c-a7b1-5c98-b215-d8e6104ffgt6@89.208.85.123:443?security=reality&type=xhttp&... - If you see
Error: Failed to fetch subscription— check headers and link.
Connecting to VPN Clients
V2RayN (Windows / Android)
- Open Subscriptions → Add Subscription
- Paste the link:
https://your-site.ru/happ_converter.php - Type: V2Ray / VLESS (or Raw)
- Click Update → servers will appear in the list
V2RayA (Linux / Docker)
- Open web interface → Subscriptions
- Click Add Subscription
- URL:
https://your-site.ru/happ_converter.php - Label:
Happ Proxy - Click Update → import configurations
Clash Meta / Mihomo
Script modification is required to output in YAML format.
For now, you can use external converters (see section below).
SingBox / Hiddify
Similar to Clash — JSON output is needed.
Temporary: copyvless://links manually.
Bypassing Device Limits
The provider counts devices by the X-Hwid header. To connect more than 3–5 devices:
Method 1: Generate a new HWID
// Insert into script or run separately:
echo bin2hex(random_bytes(16)); // 32 characters, e.g.: a1b2c3d4e5f6...
Method 2: Pass HWID via URL
-
In the script replace the line:
'X-Hwid: 74jf74nf8f4jr5je',with:
'X-Hwid: ' . ($_GET['hwid'] ?? '74jf74nf8f4jr5je'), -
Now you can connect with different HWIDs:
https://your-site.ru/happ_converter.php?hwid=abc123def456...
⚠️ Important: Do not abuse! If the provider tracks by account, not just by HWID, this may lead to a ban.
Additional Converters
If you need Clash YAML or SingBox JSON format, use external tools:
| Converter | Direction | Link |
|---|---|---|
| vless-xtls-converter | vless:// → SingBox/Clash JSON |
Open |
| amnezia_xkeen_converter | JSON → vless:// |
Open |
| VlessLinker (GitHub) | Multi-format, open source | Repository |
How to use:
- Download the list of
vless://from our script- Paste into the converter
- Download the ready config for the desired client
Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| Empty response / 502 | Wrong link, headers or blocking | Check $subscriptionUrl, compare headers with intercept, temporarily set CURLOPT_SSL_VERIFYPEER => false |
| Cache not updating | File output.cache locked or no permissions |
Delete file manually: rm output.cache, check permissions chmod 666 |
| Base64 not decoding | Server returned plain text, not Base64 | Add log: file_put_contents('debug.log', $response); and check content |
| 403 on .htaccess | Module mod_rewrite disabled or AllowOverride None |
Enable in Apache config: AllowOverride All, restart server |
| Too slow response | Timeout too small or hosting lag | Increase $timeout = 60, check server speed |
| Links not importing to client | Unsupported format | Ensure client type is V2Ray / VLESS, not Trojan/VMess |
Debug: Enable logging
Add to the beginning of the script:
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/error.log');
Security
Mandatory
- [ ] Do not publish the script in public GitHub with real tokens
- [ ] Delete
requests.loganddebug.logafter debugging - [ ] Delete
debug_headers.phpafter intercepting headers! - [ ] Restrict access to the script (if it is in public access):
// Simple basic authorization:
if (!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] !== 'admin') {
header('WWW-Authenticate: Basic realm="Happ Converter"');
header('HTTP/1.0 401 Unauthorized');
exit('Access denied');
}
- [ ] Regularly update headers if Happ changes the app version
- [ ] Use HTTPS for the domain where the script is hosted
Auto-update headers (optional)
If Happ updates frequently, you can move headers to a separate file config.php:
// config.php
return [
'user_agent' => 'Happ/3.14.0',
'device_model' => 'NEW-MODEL-2025',
// ...
];
// In the main script:
$config = require 'config.php';
$headers = [
'User-Agent: ' . $config['user_agent'],
// ...
];
File Structure
/public_html/
├── happ_converter.php # Main script
├── debug_headers.php # Header intercept (delete after use!)
├── .htaccess # Protection of service files
├── output.cache # ️ Subscription cache (created automatically)
├── requests.log # Header intercept logs (delete after use!)
├── error.log # Error logs (when debugging enabled)
├── config.php # ⚙️ Extracted settings (optional)
└── README.md # This instruction
Script Update
- Download new version of
happ_converter.php - Save your settings (
$subscriptionUrl, headers) - Replace file on server
- Clear cache:
rm output.cache - Check operation:
curl -I https://your-site.ru/happ_converter.php
Support and Questions
If something doesn't work:
- Check logs:
tail -f error.log - Test request manually:
curl -v \ -H "User-Agent: Happ/3.13.0" \ -H "X-Hwid: your_hwid" \ "https://subscription.web.tech/your_link" - Write in response:
- Error text
- PHP version (
php -v) - Web server type (Apache / Nginx)







