This guide outlines a solution for building a multi-user Cloudflare DDNS (Dynamic DNS) Hosting Platform using WordPress. The system features automatic subdomain assignment, API permission isolation, dynamic environment adaptation, and an automated mail notification system.
I. Environment Adaptation: Dynamic Domains & Cloudflare Proxy Awareness
To ensure WordPress loads resources correctly across local networks, external networks, and Cloudflare proxies, we must resolve the “Fixed URL” limitation and the “HTTPS Infinite Redirect” loop.
Configuration
Edit your wp-config.php file in the root directory. Add the following code block above the line /* That's all, stop editing! Happy publishing. */:
/* 1. Identify Cloudflare HTTPS Proxy header to fix subdomain/admin redirect issues */
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$_SERVER['HTTPS'] = 'on';
}
/* 2. Dynamically detect current access protocol */
$http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
/* 3. Force WordPress to use the current request domain (Adapts to multi-domain/IP/Intranet access) */
$current_domain = $_SERVER['HTTP_HOST'];
define('WP_HOME', $http_type . $current_domain);
define('WP_SITEURL', $http_type . $current_domain);
/* 4. Auto-repair absolute path links in the database (Buffer output replacement) */
ob_start( 'ob_replace_home_url' );
function ob_replace_home_url( $content ) {
$home_url = get_option( 'home' ); // Old domain in database
return str_replace( $home_url, WP_HOME, $content );
}
II. Core Plugin: Cloudflare Multi-Type DDNS Service (v3.2)
This plugin handles interactions with the Cloudflare API, providing each registered user with a unique API Key and permission to update their assigned subdomain.
Plugin Path: /wp-content/plugins/cf-ddns-service/cf-ddns-service.php
<?php
/*
Plugin Name: Cloudflare Multi-Type DDNS Service (Pro)
Description: A real-time DDNS management console supporting A/AAAA/CNAME updates. Integrated with unauthorized access protection, rate limiting, and domain collision checks.
Version: 3.2
Author: Gemini Thought Partner / erthanleo
*/
if (!defined('ABSPATH')) exit;
/**
* ==========================================================
* Configuration: Ensure accuracy of the following constants
* ==========================================================
*/
define('CF_DDNS_TOKEN', 'yourCFtoken');
define('CF_DDNS_ZONE_ID', 'yourCFzoneid');
define('CF_DDNS_DOMAIN', 'yourdomain');
/**
* 1. Registration Validation (Security: Length, Format, Blacklist, Collision Check)
*/
add_filter('registration_errors', 'cf_ddns_full_registration_check', 15, 3);
function cf_ddns_full_registration_check($errors, $sanitized_user_login, $user_email) {
// Length Check: Minimum 4 characters
if (strlen($sanitized_user_login) < 4) {
$errors->add('username_too_short', '<strong>Error</strong>: Username must be at least 4 characters.');
}
// Format Check: Lowercase and numbers only
if (!preg_match('/^[a-z0-9]+$/', $sanitized_user_login)) {
$errors->add('username_invalid', '<strong>Error</strong>: Usernames can only contain lowercase letters and numbers.');
}
// Reserved Keyword Blacklist Check
$keywords_str = get_option('cf_ddns_reserved_keywords', 'admin,www,api,root,blog');
$reserved = array_map('trim', explode(',', strtolower($keywords_str)));
if (in_array(strtolower($sanitized_user_login), $reserved)) {
$errors->add('username_reserved', '<strong>Error</strong>: This username is reserved by the system.');
}
// Cloudflare Collision Check (Check if domain already exists in CF)
$full_domain = $sanitized_user_login . '.' . CF_DDNS_DOMAIN;
$check_url = "https://api.cloudflare.com/client/v4/zones/" . CF_DDNS_ZONE_ID . "/dns_records?name=$full_domain";
$res = wp_remote_get($check_url, ['headers' => ['Authorization' => 'Bearer ' . CF_DDNS_TOKEN], 'timeout' => 5]);
if (!is_wp_error($res)) {
$body = json_decode(wp_remote_retrieve_body($res), true);
if (!empty($body['result'])) {
$errors->add('domain_taken', '<strong>Error</strong>: This subdomain is already occupied in Cloudflare.');
}
}
return $errors;
}
/**
* 2. Initialize API Key upon Registration
*/
add_action('user_register', function($user_id) {
$api_key = wp_generate_password(20, false);
update_user_meta($user_id, 'cf_ddns_user_key', $api_key);
});
/**
* 3. REST API Core Handler (Security: Access Control + Rate Limiting)
*/
add_action('rest_api_init', function () {
register_rest_route('cf-ddns/v1', '/update', [
'methods' => 'GET',
'callback' => 'cf_ddns_api_handler',
'permission_callback' => '__return_true',
]);
});
function cf_ddns_api_handler($request) {
$user_login_param = sanitize_user($request->get_param('user'));
$key_param = sanitize_text_field($request->get_param('key'));
$type = strtoupper($request->get_param('type') ?: 'A');
$ip = $request->get_param('ip') ?: $_SERVER['REMOTE_ADDR'];
if (!in_array($type, ['A', 'AAAA', 'CNAME'])) {
return new WP_REST_Response(['status' => 'error', 'message' => 'Unsupported record type'], 400);
}
$user = get_user_by('login', $user_login_param);
$stored_key = $user ? get_user_meta($user->ID, 'cf_ddns_user_key', true) : '';
if (!$user || $stored_key !== $key_param) {
return new WP_REST_Response(['status' => 'error', 'message' => 'Authentication failed'], 403);
}
// Rate Limiting: 10 requests every 5 minutes
$limit_key = 'cf_limit_' . $user->ID;
$count = get_transient($limit_key) ?: 0;
if ($count >= 10) {
return new WP_REST_Response(['status' => 'error', 'message' => 'Rate limit exceeded: 10 reqs per 5 mins'], 429);
}
set_transient($limit_key, $count + 1, 300);
$full_domain = strtolower($user->user_login) . '.' . CF_DDNS_DOMAIN;
$query_url = "https://api.cloudflare.com/client/v4/zones/" . CF_DDNS_ZONE_ID . "/dns_records?name=$full_domain&type=$type";
$query_res = wp_remote_get($query_url, ['headers' => ['Authorization' => 'Bearer ' . CF_DDNS_TOKEN]]);
$query_data = json_decode(wp_remote_retrieve_body($query_res), true);
$record_id = (!empty($query_data['result'])) ? $query_data['result'][0]['id'] : null;
$api_url = "https://api.cloudflare.com/client/v4/zones/" . CF_DDNS_ZONE_ID . "/dns_records" . ($record_id ? "/$record_id" : "");
$method = $record_id ? 'PUT' : 'POST';
$response = wp_remote_request($api_url, [
'method' => $method,
'headers' => ['Authorization' => 'Bearer ' . CF_DDNS_TOKEN, 'Content-Type' => 'application/json'],
'body' => json_encode(['type' => $type, 'name' => $full_domain, 'content' => $ip, 'ttl' => 120, 'proxied' => false])
]);
$res_data = json_decode(wp_remote_retrieve_body($response), true);
if (!empty($res_data['success'])) {
return new WP_REST_Response(['status' => 'success', 'domain' => $full_domain, 'remaining' => (10 - ($count + 1))], 200);
}
return new WP_REST_Response(['status' => 'error', 'message' => 'Cloudflare API interaction failed'], 500);
}
/**
* 4. Frontend UI Management Console [display_ddns_info]
*/
add_shortcode('display_ddns_info', 'cf_ddns_render_ui');
function cf_ddns_render_ui() {
if (!is_user_logged_in()) return '<p>Please <a href="'.wp_login_url(get_permalink()).'">Login</a> to view DNS info.</p>';
$user = wp_get_current_user();
$key = get_user_meta($user->ID, 'cf_ddns_user_key', true);
$subdomain = $user->user_login . '.' . CF_DDNS_DOMAIN;
$res = wp_remote_get("https://api.cloudflare.com/client/v4/zones/".CF_DDNS_ZONE_ID."/dns_records?name=$subdomain", [
'headers' => ['Authorization' => 'Bearer ' . CF_DDNS_TOKEN]
]);
$records = json_decode(wp_remote_retrieve_body($res), true)['result'] ?? [];
ob_start(); ?>
<div style="border: 1px solid #e1e4e8; padding: 25px; border-radius: 12px; background: #fff; box-shadow: 0 4px 12px rgba(0,0,0,0.05); margin: 20px 0;">
<h3 style="margin-top:0; color:#24292e;">Domain Management Console</h3>
<p><strong>Assigned Domain:</strong> <code style="color:#0366d6; font-size:16px;"><?php echo $subdomain; ?></code></p>
<p><strong>Your API Key:</strong> <code style="background:#fff5b1; padding:2px 5px; border-radius:3px;"><?php echo $key; ?></code></p>
<table style="width:100%; border-collapse: collapse; margin-top: 20px;">
<tr style="background: #f6f8fa;">
<th style="padding:12px; border: 1px solid #d1d5da; text-align: left;">Type</th>
<th style="padding:12px; border: 1px solid #d1d5da; text-align: left;">Current Value</th>
<th style="padding:12px; border: 1px solid #d1d5da; text-align: left;">Action</th>
</tr>
<?php foreach(['A', 'AAAA', 'CNAME'] as $t):
$val = '<span style="color:#ccc;">Not Set</span>';
foreach($records as $rec) {
if($rec['type'] === $t) {
$val = '<strong style="color:#22863a;">' . $rec['content'] . '</strong>';
break;
}
}
$link = home_url("/wp-json/cf-ddns/v1/update?user={$user->user_login}&key={$key}&type={$t}&ip=YOUR_IP");
?>
<tr>
<td style="padding:12px; border: 1px solid #d1d5da;"><strong><?php echo $t; ?></strong></td>
<td style="padding:12px; border: 1px solid #d1d5da;"><?php echo $val; ?></td>
<td style="padding:12px; border: 1px solid #d1d5da;">
<button style="cursor:pointer; background:#2ea44f; color:#fff; border:none; padding:6px 12px; border-radius:6px;"
onclick="navigator.clipboard.writeText('<?php echo $link; ?>').then(()=>alert('API URL Copied'))">Copy Link</button>
</td>
</tr>
<?php endforeach; ?>
</table>
<div style="margin-top:25px; font-size:13px; color:#586069; border-top:1px solid #eaecef; padding-top:15px; line-height:1.6;">
<h4 style="margin-top:0; color:#24292e;">User Guide:</h4>
1. Click <strong>"Copy Link"</strong> to get your unique update URL.<br>
2. <strong>Manual Update:</strong> Append <code>&ip=YOUR_IP</code> to the link and visit it in a browser.<br>
3. <strong>Routers/Scripts:</strong> If <code>&ip=</code> is omitted, the system auto-detects your exit IP.<br>
4. <strong>TTL:</strong> Cloudflare updates usually propagate within 2-5 minutes.<br>
5. <strong>Verify:</strong> Test via terminal:<br>
<code>nslookup <?php echo $subdomain; ?> 1.1.1.1</code><br>
<code>nslookup <?php echo $subdomain; ?> 2606:4700:4700::1111</code>
</div>
</div>
<?php return ob_get_clean();
}
/**
* 5. Admin Panel & Security Permissions
*/
add_action('admin_init', function() {
if (!current_user_can('administrator') && !(defined('DOING_AJAX') && DOING_AJAX)) {
wp_safe_redirect(home_url()); exit;
}
});
add_action('after_setup_theme', function() {
if (!current_user_can('administrator')) show_admin_bar(false);
});
add_action('admin_menu', function() {
add_options_page('DDNS Settings', 'DDNS Settings', 'manage_options', 'cf-ddns-settings', function() {
?>
<div class="wrap"><h1>DDNS Settings</h1><form method="post" action="options.php">
<?php settings_fields('cf_ddns_settings_group'); do_settings_sections('cf-ddns-settings'); submit_button(); ?>
</form></div>
<?php
});
});
add_action('admin_init', function() {
register_setting('cf_ddns_settings_group', 'cf_ddns_reserved_keywords');
add_settings_section('cf_ddns_main', 'Security Controls', null, 'cf-ddns-settings');
add_settings_field('keywords', 'Reserved Keywords (Comma separated)', function() {
$val = get_option('cf_ddns_reserved_keywords', 'admin,www,api,root');
echo '<textarea name="cf_ddns_reserved_keywords" rows="5" class="large-text">' . esc_textarea($val) . '</textarea>';
}, 'cf-ddns-settings', 'cf_ddns_main');
});
/**
* 6. Clean up Cloudflare records on User Deletion
*/
add_action('delete_user', function($user_id) {
$user = get_userdata($user_id);
if (!$user) return;
$domain = $user->user_login . '.' . CF_DDNS_DOMAIN;
$res = wp_remote_get("https://api.cloudflare.com/client/v4/zones/".CF_DDNS_ZONE_ID."/dns_records?name=$domain", ['headers'=>['Authorization'=>'Bearer '.CF_DDNS_TOKEN]]);
$data = json_decode(wp_remote_retrieve_body($res), true);
if (!empty($data['result'])) {
foreach($data['result'] as $r) {
wp_remote_request("https://api.cloudflare.com/client/v4/zones/".CF_DDNS_ZONE_ID."/dns_records/".$r['id'], ['method'=>'DELETE', 'headers'=>['Authorization'=>'Bearer '.CF_DDNS_TOKEN]]);
}
}
});
III. Complementary Settings
User Management Optimization
- Enable Registration:
Settings->General->Membership(Check “Anyone can register”). - Permalinks:
Settings->Permalinks(Select “Post name”). This is crucial for REST API routing.
Security Patches
- Admin Access: Ordinary users are redirected away from
/wp-admin(handled by the plugin). - Toolbar: Admin toolbar is hidden for non-administrators.
Mail System (Gmail API Integration)
- Objective: Solve the high bounce/spam rate of default PHP mailers to ensure users receive activation links.
- Workflow:
- Google Cloud Console -> Enable Gmail API.
- Create OAuth 2.0 Credentials.
- Authorize the WordPress plugin to send emails via your Google account.
IV. Critical Checklist
Cloudflare Configuration
- SSL/TLS Mode: Use Full if the origin has a certificate; use Flexible if it does not (paired with the proxy-awareness code in
wp-config.php). - Edge Certificates: Enable Always Use HTTPS and Automatic HTTPS Rewrites.
- Page Rules: Ensure no rules targeting the root directory interfere with subdomain or subdirectory traffic.
Plugin Verification
- Domain Constant: Ensure
CF_DDNS_DOMAINdoes not include a protocol (e.g.,example.com, nothttps://example.com).