ps.ps is not a product, a scam, or a Photoshop tool. It is a Chinese-language photography blog:
27 short tutorial posts, a Z-BlogPHP install last updated in 2024, and a six-character domain that only
exists because .ps happens to look like PS. This page is the whole thing — every page
and every asset mirrored file-for-file — plus what the infrastructure around it does and does not reveal.
.ps is the ccTLD for Palestine, run by PNINA. Register ps.ps and you own a
domain that reads as “PS” — Photoshop — with a dot in the middle. The site is a photography blog,
so the name is doing exactly one job: being a six-character joke that a photographer can type.
The masthead reads 凤翎公子 — 专注人像摄影 (“Fengling Gongzi — focused on portrait photography”). The author name is the same, and the keywords list the real editorial plan: 摄影基础 (photography basics), 人像摄影 (portraits), 摄影后期 (post-processing), 摄影器材 (gear), 手机摄影 (phone photography), 化妆技巧 (makeup), 主题妆造 (themed styling).
| Category | URL | Posts | What it covers |
|---|---|---|---|
| 摄影 | ?cate=1 | 20 | Exposure, ISO, shutter, metering, composition, light, depth of field |
| 人像 | ?cate=2 | 7 | Poses, 三庭五眼 facial proportions, 古风 / 复古法式 / 日系 / 欧美 styling shoots |
| 妆造 | ?cate=4 | 1 | A ten-step everyday makeup routine |
| 关于 | ?id=3 | 1 | About page |
The posts are short and instructional, not a portfolio: the median post is 496 Chinese characters, the longest is about 765, and the shape is always the same — a numbered list of steps with the reasoning attached. “化妆步骤” (makeup steps) is literally ten ✅-prefixed steps, “相机五大档位” is five camera modes, “九种摄影构图” is nine compositions. Photography-as-recipe.
Dates run 2025-07-22 → 2026-09-06, and the distribution is the story: 24 of the 28 posts land
in February 2026, 16 of them on a single day (2026-02-03), then one post in June and one in
September. That is not a publishing rhythm; that is a content dump. Two IDs in the sequence
(id=2, id=21) and two categories (cate=3, cate=5) return
404 at the origin — deleted or never published, and Z-BlogPHP happily renumbered around the holes.
That single line is the most informative sentence on the site. This is aggregated content — the ten-step makeup routine is not the author's own shoot, and the photo credits belong to whoever took them. It also explains the volume: 16 photography explainers on one calendar day, all in the same numbered-steps template.
| Layer | What it is | Evidence |
|---|---|---|
| CMS | Z-BlogPHP 1.7.4 | /zb_system/ scripts, zbpConfig, ?id= routing |
| Theme | TztCard, build dated 2024-02-27 | zb_users/theme/TztCard/, ?2024-02-27 cache-bust |
| JS | jQuery 2.2.4, viewer.min.js lightbox, jquery.qrcode | script tags in <head> |
| Fonts | Iconfont (32 KB ttf) | style/font/iconfont.ttf |
| Server | nginx, no X-Powered-By | response headers |
| TLS | Certum DV TLS G2 R39 CA (Asseco Data Systems, PL), valid to 2027-03-28 | openssl s_client |
| Transport | HTTP → HTTPS 301, Strict-Transport-Security: max-age=31536000 | curl -I |
Two small choices worth noting. There is no login form rendered and no
comment UI in the templates — a blog with the social surface switched off, which is why there is nothing on
the site for a visitor to break. And the PHP version is deliberately not advertised
(no X-Powered-By), even though the CMS version is still exposed in the asset paths.
This is the part where a “check the location” answer usually goes wrong, so here is the full evidence chain rather than a flag on a map.
Those answers disagree, and both are correct: the IP block is allocated to a Shanghai company and announced through a Hong Kong transit provider (Cloudie, AS55933). Geolocation APIs report the network operator's facility, not the owner. Country-level is usually right; city-level is a rack number.
sitemap.xml, no date/author metadata worth trustingReading this site is harmless. Logging into it would not be — and luckily there is nothing to log into.
Every page reachable from the homepage was fetched with a plain HTTP client and stored under a deterministic path, so the site works with no server, no PHP and no network. 53 pages, 84 assets, 10.6 MB — of which 9.8 MB is imagery: the article photographs plus the theme's generated thumbnails, which is where most of the weight sits.
| Origin URL | Mirror path | Count |
|---|---|---|
https://ps.ps/ | index.html | 1 |
/?id=N | p/N.html | 31 |
/?cate=N, ?cate=N&page=M | cate/N.html, cate/N-M.html | 7 |
/?page=N | page/N.html | 6 |
/?date=YYYY-MM | date/YYYY-MM.html | 8 |
/zb_users/…, /zb_system/… | same path, relative | 84 |
What was changed, exhaustively: the five rewrites below, and nothing else. Every other byte — every article, every image, every stylesheet, every script — is what the server sent.
?id=)c_html_js_add.php → c_html_js_add.js (served as JavaScript, not downloaded as PHP), with bloghost and ajaxurl blanked so the mirror can never POST to the live blog# with onsubmit="return false"; search is server-side and cannot exist in a mirrorfeed.php → feed.xml, so the RSS link still opens as XMLFidelity check: each page's origin bytes were re-fetched and hashed at build time,
and the MIRROR.json in the folder records URL → path → sha256 for all 137 files — 83
byte-identical, 54 differing only inside URL attributes, 0 unexplained. One caveat, measured over the
wire: the host injects one line into every HTML page it serves (a Cloudflare Web Analytics
<script>), so the archived file is byte-identical to the origin while the copy served
from this portfolio is not. The difference is exactly one script tag at the end of <head>.
The two deleted IDs and two deleted categories are 404 at the origin, so they are 404 here. There is no
sitemap to prove the post list is complete — completeness is defined as “everything linked from the
homepage and its pagination”, which is 53 pages. Category ?cate=1 runs to four pages and
?cate=2 to two; both were followed to the end. This is a snapshot taken on
2026-10-21: if the blog posts again, the mirror will not know.