A lightweight, framework-free PHP admin panel for managing DNS records across multiple Cloudflare accounts — including reverse DNS (in-addr.arpa / PTR) workflows.
Built for simple hosting environments: no Composer, no database server. Data lives in JSON files with file locking (flock).
- Multi-account Cloudflare support via API Token
- Domains listed per account, with record counts and reverse-zone IP ranges
- DNS create / edit / delete against the Cloudflare API (single and bulk)
- Local JSON cache refreshed by cron (pages stay fast — no blocking sync on UI)
- Separate sync cron and PTR audit cron with independent locks
- Operators with customizable access levels, rank hierarchy, and per-domain ACL
- IP / subnet allowlist for login and panel access
- Reverse DNS tools: IP-range detection, IP → PTR lookup + create-when-missing, PTR consistency audit / auto-delete
- Separated log channels: Audit, DNS changes, PTR, and Cron
- Paginated logs with 20 / 50 / 100 per-page selector
- Change your own password from the panel
- Compatible with PHP 7.4+
| Requirement | Details |
|---|---|
| PHP | 7.4 or newer |
| Extensions | curl, json, session |
| Web server | Apache, Nginx, LiteSpeed, or PHP built-in server |
| Permissions | Write access to the data/ directory |
| Cloudflare | API Token with Zone + DNS permissions |
No MySQL/PostgreSQL and no Composer dependencies.
git clone https://github.com/MrAriaNet/CloudFlare-Mini.git
cd CloudFlare-MiniPoint your web server document root to the public/ folder.
| Correct | Incorrect |
|---|---|
.../CloudFlare-Mini/public |
.../CloudFlare-Mini (project root) |
If the document root must stay on the project root, the included root .htaccess rewrites requests into public/ (Apache). Prefer pointing directly at public/ when possible.
chmod -R 755 data
# If needed for your host:
chmod -R 775 dataThe application auto-creates JSON files on first run, including:
| File | Purpose |
|---|---|
accounts.json |
Cloudflare accounts / API tokens |
zones.json |
Cached zones per account |
records.json |
Cached DNS records per zone |
operators.json |
Panel users |
roles.json |
Access levels / permissions |
ip_allowlist.json |
Allowed IPs / CIDRs |
ptr_settings.json |
PTR audit settings |
ptr_issues.json |
Open PTR mismatch list |
logs.json |
Audit logs |
dns_logs.json |
Forward DNS create / edit / delete |
ptr_logs.json |
PTR audit / create / delete |
cron_logs.json |
Sync cron jobs |
meta.json |
Sync metadata |
Default administrator (created automatically when no operators exist):
| Field | Value |
|---|---|
| Username | admin |
| Password | admin123 |
Change this password immediately via Change Password (or Operators → Edit).
- Set a strong
cron_keyinapp/config.php. - Optionally configure IP Allowlist so only trusted networks can reach the panel.
- Go to Accounts → Add account
- Enter a display name
- Paste a Cloudflare API Token
- Save — the panel verifies the token and syncs zones / DNS
Create a token at: Cloudflare API Tokens
Suggested permissions:
- Zone → Zone → Read
- Zone → DNS → Edit
Include the zones (or account) you want this panel to manage.
cd public
php -S 0.0.0.0:8080Visit: http://localhost:8080
Self-check (optional):
php scripts/selfcheck.phpAdd as many accounts as you need. Each account keeps its own API token. Domains are always grouped under their parent account. The Accounts and Dashboard views show status, domain counts, and last sync time.
- Forward zones and reverse (
in-addr.arpa) zones are labeled clearly - Reverse zones show the derived IP range
- Each domain shows a record count from the local cache
- Filter by account or search by domain / IP range
Supported record types:
A, AAAA, CNAME, TXT, MX, NS, SRV, CAA, PTR
Per domain you can:
- List records (with type / proxy / TTL badges)
- Create a single record
- Edit / delete a single record
- Bulk add up to 200 records per submit
- Bulk edit selected records in a table form
- Refresh that domain’s records from Cloudflare
Mutations call Cloudflare immediately. On success, the local JSON cache is updated in place. Successful DNS create / edit / delete events are written to DNS Change Logs (not the general audit log).
One record per line. Columns:
type,name,content,ttl,proxied,priority
- Separators: CSV (
,), pipe (|), or tab ttl,proxied, andpriorityare optional- Lines starting with
#are ignored - Header row
type,name,...is skipped
Examples:
# examples
A,www,203.0.113.10,1,0,
A,api,203.0.113.11,1,1,
CNAME,blog,www.example.com,1,0,
MX,@,mail.example.com,1,0,10
TXT,@,"v=spf1 include:_spf.google.com ~all",1,0,
PTR,10,host.example.com,1,0,
proxied accepts 1 / true / yes / proxied / on.
- Open a domain’s DNS records
- Select one or more rows
- Click Bulk edit selected
- Update fields in the grid and save
Limited to 200 records per submit. Partial failures are reported without discarding successful updates.
Page loads never call the Cloudflare sync API in the background. The panel always reads from the local JSON cache so login and navigation stay fast.
| Mode | Behavior |
|---|---|
| Cron (recommended) | Refresh cache every hour in the background |
| Manual sync | Accounts → Sync or Dashboard → Force sync all accounts |
| Per-domain refresh | DNS Records → Refresh from Cloudflare |
| On account add/edit | One-time sync for that account only |
DNS create / edit / delete still hit Cloudflare immediately, then update the cache.
Failed syncs are logged and do not wipe previously cached good data.
Schedule two separate jobs (do not combine PTR into sync):
0 * * * * /usr/bin/php /path/to/CloudFlare-Mini/scripts/cron.php >/dev/null 2>&1
15 * * * * /usr/bin/php /path/to/CloudFlare-Mini/scripts/cron_ptr.php >/dev/null 2>&1| Script | Purpose |
|---|---|
scripts/cron.php |
Cloudflare account / zone / DNS cache sync |
scripts/cron_ptr.php |
PTR consistency audit (+ optional auto-delete) |
Each job has its own lock file so they cannot duplicate or overlap themselves.
Force anytime:
php scripts/cron.php --force
php scripts/cron_ptr.php --force- Set a strong secret in
app/config.php:
'cron_key' => 'put-a-long-random-secret-here',- Schedule these URLs every hour (separate jobs):
https://your-domain.example/cron.php?key=put-a-long-random-secret-here
https://your-domain.example/cron_ptr.php?key=put-a-long-random-secret-here
Optional force:
https://your-domain.example/cron.php?key=YOUR_SECRET&force=1
https://your-domain.example/cron_ptr.php?key=YOUR_SECRET&force=1
- Automatic detection of
in-addr.arpareverse zones and their IP ranges - Find PTR by IPv4 on the Domains page
- Create PTR when no record exists (pick covering reverse zone + hostname)
- PTR Audit: forward-check each PTR hostname (FCrDNS via DNS; optional ICMP ping on manual runs)
- Open mismatches listed for review, dismiss, or delete
- Optional auto-check / auto-delete via
cron_ptronly
Available under PTR Audit (requires permissions):
| Setting | Meaning |
|---|---|
| Enable auto PTR audit | Honored by cron_ptr only |
| Auto-check interval | Minimum seconds between auto runs (default 21600 = 6 hours) |
| Auto-delete mismatched PTRs | Delete broken PTR records during audit / PTR cron |
| Use ICMP ping | Manual audit only — PTR cron always uses fast DNS-only checks |
PTR cron processes records in batches (ptr_audit_batch in config) so large reverse zones do not hang the job.
All PTR activity (audit summaries, creates, deletes, settings changes) is stored only in data/ptr_logs.json and shown under PTR Logs. It never appears in Audit / DNS / Cron logs.
Access is controlled by customizable roles stored in data/roles.json.
| Role | Rank | Can create operators up to | Typical use |
|---|---|---|---|
| Administrator | 100 | Any level (including admin) | Full system control |
| Manager | 60 | Rank ≤ 40 (Editor / Viewer) | Team lead who creates staff |
| Editor | 40 | — | DNS create / edit / delete |
| Viewer | 20 | — | Read-only |
Permissions are grouped in the Access Levels UI:
Accounts & security
- Manage Cloudflare accounts
- Sync a single Cloudflare account
- Force sync all accounts
- Manage IP allowlist
Domains & DNS
- View domains / DNS records
- Create / edit / delete DNS
- Refresh DNS from Cloudflare
- View PTR mismatch list
- Run PTR consistency audit
- Configure PTR auto-check / auto-delete
- Delete mismatched PTR records
Operators
- Manage operators
- Define access levels (roles)
Logs
- View audit / DNS / cron / PTR logs
- View everyone’s logs (not only own)
- Clear each log channel separately
Each role also has a max assignable rank — a hard ceiling for roles that user may assign to others.
A user cannot assign an access level that is:
- Higher than their own rank, or
- Higher than their role’s max assignable rank
Example: Manager (rank 60, max assignable 40) can create Editor and Viewer, but not Manager or Administrator.
For each operator (except full admin-rank accounts) you can choose:
- All domains — every synced zone
- Selected domains — only chosen zone IDs
- Any logged-in user can change their own password under Change Password
- Users who manage operators can reset passwords for operators within their allowed rank
Under IP Allowlist, restrict panel access to specific IPv4 addresses or CIDR subnets (for example 203.0.113.0/24). When configured, requests from other IPs are denied before login.
Four separate channels — PTR never mixes with the others:
| Channel | File | Contents |
|---|---|---|
| Audit Logs | data/logs.json |
Login / logout / failures, accounts, operators, roles, password, DNS refresh, IP allowlist, clears |
| DNS Change Logs | data/dns_logs.json |
Forward DNS create / edit / delete (including bulk) |
| PTR Logs | data/ptr_logs.json |
PTR audit, create, delete, settings |
| Cron Logs | data/cron_logs.json |
Background sync jobs only |
Notes:
- Users with view all logs see everyone’s entries (where applicable)
- Others typically see only their own operator actions
- Each channel can be cleared independently (with permission)
- List pages support search and pagination (20 / 50 / 100 per page)
- Older PTR-related rows that landed in other channels are migrated into PTR Logs automatically
Edit app/config.php:
| Key | Default | Description |
|---|---|---|
app_name |
Cloudflare Mini DNS Panel | Application title |
sync_interval |
3600 |
How long cache is considered fresh (seconds) |
web_lazy_sync |
false |
If true, pages may sync on load (slow — keep false) |
cron_key |
(change me) | Secret for /cron.php and /cron_ptr.php HTTP cron |
cron_lock_ttl |
900 |
Seconds before a stuck cron lock is considered stale |
cron_time_limit |
600 |
PHP time limit for a cron run |
ptr_audit_batch |
300 |
Max PTR records checked per auto PTR cron run |
default_admin.username |
admin |
Seeded only when no operators exist |
default_admin.password |
admin123 |
Seeded only when no operators exist |
dns_types |
A, AAAA, CNAME, … | Allowed DNS types in the UI |
login_max_attempts |
8 |
Failed logins before temporary lock |
login_lock_seconds |
300 |
Lock duration after too many failures |
After the first successful boot, changing default_admin does not reset an existing admin user.
CloudFlare-Mini/
├── app/
│ ├── bootstrap.php # App bootstrap + JSON store seed
│ ├── config.php # Configuration
│ ├── Auth.php # Login / sessions
│ ├── Access.php # Permissions & domain ACL helpers
│ ├── JsonStore.php # Atomic JSON storage with flock
│ ├── RoleService.php # Access levels / permissions
│ ├── CloudflareClient.php # Cloudflare API v4 client
│ ├── SyncService.php # Account / zone / DNS sync + cache
│ ├── PtrService.php # Reverse DNS helpers + audit
│ ├── PtrCronRunner.php # PTR cron runner
│ ├── CronRunner.php # Sync cron runner
│ ├── CronLock.php # Non-overlapping cron locks
│ ├── IpAllowlist.php # IP / CIDR allowlist
│ ├── Logger.php # Multi-channel logger
│ ├── helpers.php # Shared helpers (URL, CSRF, pagination, …)
│ ├── controllers/ # Route controllers
│ └── views/ # English HTML templates
├── public/
│ ├── index.php # Front controller (web root)
│ ├── cron.php # HTTP sync cron (?key=...)
│ ├── cron_ptr.php # HTTP PTR cron (?key=...)
│ └── assets/ # CSS / JS
├── data/ # JSON database (not for public access)
├── scripts/
│ ├── cron.php # CLI sync cron (recommended)
│ ├── cron_ptr.php # CLI PTR cron (recommended)
│ ├── sync.php # Alias → cron.php
│ └── selfcheck.php # Basic install check
├── .gitignore
├── .htaccess # Optional rewrite to public/
└── README.md
No special rewrite is required for query-string routing (index.php?r=...).
Ensure AllowOverride is enabled if you rely on .htaccess.
server {
listen 80;
server_name dns-panel.example.com;
root /var/www/CloudFlare-Mini/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php7.4-fpm.sock; # adjust version/socket
}
# Deny access if someone mispoints root at the project folder
location ~ ^/(app|data|scripts)/ {
deny all;
}
}- Treat
data/as sensitive — it stores API tokens and password hashes - Never expose
data/,app/, orscripts/as a public URL - Change the default admin password on first login
- Change
cron_keybefore enabling HTTP cron endpoints - Use least-privilege Cloudflare API tokens
- Prefer enabling the IP allowlist in production
- CSRF tokens are required on POST forms
- Login attempts are rate-limited in-session
- Prefer HTTPS in production
- Keep PHP and the host patched
| Problem | What to check |
|---|---|
| Redirect loop | Make sure you open .../public/index.php (or a vhost rooted at public/). |
| Blank page / 500 | Enable PHP error display temporarily; confirm curl + json extensions; ensure data/ is writable |
| Cannot add account | Token invalid or missing Zone/DNS permissions |
| Domains missing for an operator | Operator domain access may be set to Selected with no zones chosen |
| Cache looks stale | Schedule cron.php, use Force sync, or open /cron.php?key=... |
| PTR audit never runs | Schedule cron_ptr.php separately; enable auto-check; wait for interval; try --force |
| Login / pages are slow | Keep web_lazy_sync = false and use cron for background sync |
| Access denied (IP) | Your client IP is outside the configured allowlist |
| Permission denied writing JSON | Fix ownership/permissions on data/ for the PHP/web user |
| Bulk create/edit partial errors | Check Cloudflare API responses in the flash message; fix invalid rows and retry |
- Plain PHP 7.4+ (no framework)
- Session-based authentication
- JSON file storage with
flock - Cloudflare API v4 over cURL
- Vanilla HTML / CSS / JS front end
- Export / import zone snapshots
- Per-account sync schedule overrides
- 2FA for operators
- Webhook / Telegram notifications on DNS or PTR changes
- IPv6 reverse DNS (
ip6.arpa) helpers
Contributions and issue reports are welcome.
This project is released under the MIT License.
This project is not affiliated with or endorsed by Cloudflare, Inc.
“Cloudflare” is a trademark of Cloudflare, Inc.