AI 资讯
GrapheneOS 2027: Premium Phones Get Real‑World Privacy
GrapheneOS 2027 Lands on Flagship Phones: Real‑World Privacy for Premium Android Users Introduction When GrapheneOS announced official support for Motorola, OnePlus and Sony’s top‑tier phones in early 2027, the tech community stopped scrolling. Within days the phrase “GrapheneOS Motorola” spiked 250 % on Google Trends and sparked a firestorm on Hacker News. Why the hype? Because for the first time a hardened, auditable Android fork is available on devices that don’t compromise on performance, camera quality, or design. In this guide you’ll get a hands‑on look at what GrapheneOS 2027 actually does, how to install it, and which commands and configuration tweaks let you turn a flagship phone into a privacy‑first workstation. Quick‑Start Checklist ✅ Item 1 Verify device compatibility (locked bootloader, Snapdragon 8 Gen 3, TEE) 2 Backup current ROM (e.g., adb backup -apk -shared -all -f backup.ab ) 3 Unlock bootloader ( fastboot oem unlock ) – note this wipes data 4 Flash GrapheneOS boot and system images (see “Flashing the ROM”) 5 Enable verified boot ( fastboot flashing lock ) 6 Install the optional Play Store Compatibility Layer (PSCL) if needed Supported Premium Devices (2027) Manufacturer Model Key Security HW Motorola Edge 30 Ultra Snapdragon 8 Gen 3, TEE, Secure Enclave OnePlus 12 Pro Snapdragon 8 Gen 3, TEE, Secure Enclave Sony Xperia 1 V Snapdragon 8 Gen 3, TEE, Secure Enclave Google Pixel 9 (reference) Snapdragon 8 Gen 3, Titan M2 All listed phones meet GrapheneOS’s Hardware Security Module (HSM) requirements: locked bootloader, hardware‑backed keystore, and a modern Trusted Execution Environment. How GrapheneOS Differs from Stock Android Feature Stock Android GrapheneOS 2027 Google Play Services Core system component, heavy telemetry Replaced by a sandboxed Play Store Compatibility Layer (PSCL) Kernel Standard Linux kernel with optional vendor patches Memory‑safe, mitigates Spectre/Meltdown, SELinux Enforcing by default App Sandbox Permissions granted per‑app
AI 资讯
Three Lines to Draw Before You Scrape Instagram
Most write-ups on this subject are about technique. This one is about the three decisions you should make before you write any code, because in my experience every project that went badly went badly for a reason that was decided on day one and not noticed until much later. I have built this kind of collection twice, for competitive analysis and for a partner-vetting workflow. Neither of them needed to touch anything behind a login, and I want to explain why that turned out to be the useful constraint rather than the limiting one. Line one: the login wall is a boundary A login wall is a statement about who the content is for. Treating it as an engineering obstacle to be routed around is the decision that puts a project on the wrong side of everything: terms of service, the platform's own detection, and in several jurisdictions the law. So the first line is simply: if it requires an account to see, it is out of scope. Not "hard," not "for later." Out of scope. I am not going to discuss techniques for getting past one, and I would be sceptical of any article that does. The interesting engineering question here is not how to see more. It is how much you can actually do with what is openly published, and the honest answer is: considerably more than people assume before they check. This constraint also has a practical benefit that is easy to miss. A pipeline built only on openly available data does not break when authentication changes, does not require credential management, and does not put an account at risk. Mine has survived two platform changes that took down colleagues' authenticated collectors. Line two: public does not mean unrestricted The second line is the one developers get wrong most often, and it has nothing to do with access. Data being publicly visible says nothing about whether you may store it, for how long, or what you may do with it. In the EU and UK, information about an identifiable person is personal data whether or not they published it themselves
科技前沿
Mirrorless vs DSLR: Which camera type is better?
The industry seems to be moving on from DSLR technology, but is it time to ditch your DSLR camera yet?
AI 资讯
This R-Rated Film Studio Wants to Be the HBO of AI
Rogue Studios, a new cinematic adult AI-generator, is betting big on the future of “sophisticated” spicy content.
AI 资讯
My evidence pipeline was saving Cloudflare block pages as evidence
I build a web service that preserves evidence of harassment on social platforms. The core feature is a single thing: automatically capture a real screenshot of the offending post. There was no substitute for it. I built an alternative that pulled the text through an API and rendered a tidy "evidence card" image, and threw it away. An image you can author freely afterwards proves nothing. Here's the conclusion first. Third-party wrappers eventually die, and when they do, the failure comes back as a plausible-looking image rather than an error. The first approach was refused by the other side I started with Cloudflare Browser Rendering. The wiring worked. The capture didn't. X blocks headless browsers. The request times out YouTube refuses script injection under a Trusted Types CSP. There's no way to make it render the comment Neither is a bug in my implementation — that is how they are built. So I declared Cloudflare alone impossible for this and moved to a service with a real browser and bot avoidance behind it. Both captures started working. For X, open the post page and clip the tweet element. For YouTube, open the URL with &lc= and screenshot just that comment element. Element screenshots have one trap worth knowing: selector_algorithm=clip returns a blank image when the element sits below the fold. The selector matches, the capture "succeeds," and the file is empty. That took a while to see. ytd-comment-thread-renderer :has ( a [ href *= "lc=ID" ]) A parameter that had worked started returning 400 I wanted timestamps rendered in Japan time, so I passed time_zone: Asia/Tokyo . One day every request started coming back 400. Every capture failed. The provider had narrowed which timezones they accept. Nothing changed on my side. I could diagnose it immediately only because I was storing the raw error body in the database. The response went into rawPayload.screenshotError , so opening one row told me why. Without that, this starts as "captures stopped working, no ide
AI 资讯
One Ciphertext, Two Valid Plaintexts: Why AEAD Needs Key Commitment
Modern encryption is almost always AEAD: authenticated encryption with associated data. AES-GCM and ChaCha20-Poly1305 are the two you meet everywhere, in TLS, in disk encryption, in message formats, in cloud key management. They give you confidentiality plus an authentication tag, and decryption either returns the plaintext or returns an error. The security definition behind that tag is about forgery. An attacker who does not know the key cannot produce a ciphertext that verifies. That definition holds. What it says nothing about is the situation where the attacker does know one or more keys and gets to choose the ciphertext. What a key multi-collision looks like Take AES-GCM. Its authentication tag is computed with GHASH, a polynomial evaluation over a binary field, and the relationship between the ciphertext blocks and the tag is linear in that field. Linearity is convenient for speed, and it is also solvable. Given two keys the attacker controls, K1 and K2, that linearity lets them set up a system of equations and solve for a ciphertext whose tag verifies under both. Decrypting it with K1 yields one plaintext. Decrypting the same bytes with K2 yields a completely different plaintext. Neither decryption throws an error, because from each key's point of view the tag is correct. Both plaintexts can be attacker-chosen and meaningful. The 2019 paper that named this attack demonstrated a file that was a valid image either way, which is where the memorable label came from: the two decryptions showed different pictures, and the second one had a salamander in it that the reporting system never saw. The property that was missing. An AEAD is key committing if a ciphertext can verify under at most one key. AES-GCM, AES-GCM-SIV, and ChaCha20-Poly1305 are not key committing, and were never claimed to be. The property simply was not part of the design goal, and for a long time no widely deployed system depended on it. The system it broke: message franking Here is the problem th
AI 资讯
LLM-Generated GraphQL Mocks Arrive at Airbnb and Expedia, While the Spec Lags Behind
Expedia Group has open-sourced mockql-rs, a Rust CLI that fills @mock-annotated GraphQL fields with LLM-generated data at request time. It follows Airbnb's @generateMock in April and a GraphQL Foundation RFC opened in February. All three solve the same problem with different architectures, and two use the same directive name with incompatible semantics. By Steef-Jan Wiggers
AI 资讯
We Almost Deployed a Temporal Knowledge Graph. The Eval Said No.
The eval that killed the temporal knowledge graph asserted one thing: at time T, the agent should report the state that was true at T. It failed 41% of the time. The graph had the right facts. It just handed the agent the wrong one. That number is what saved us from shipping. Every static retrieval metric looked fine. The graph answered "what is the status of Node A" with a confident, well-formed response. Trouble is, "what is the status" is a temporal question wearing a static question's clothes, and nothing in our test suite had noticed the difference until we wrote a test that actually asked about time. What I expected The pitch for a temporal knowledge graph (TKG) is genuinely good. You store facts as quadruples instead of triples: (subject, predicate, object, timestamp) or, better, (subject, predicate, object, valid_from, valid_to) . Now your agent memory isn't a flat pile of embeddings, it's a structured record of what was true and when. This is the natural next step past pure vector recall, and it slots neatly into the decay-based thinking I've written about before in Eviction Without Deletion . Instead of letting old facts fade by activation weight, you make validity windows explicit. My hope was that the graph would fix the exact failure mode that plagues flat vector memory: the agent confidently recalling a stale fact because it's semantically close to the query. With valid_from and valid_to on every edge, staleness becomes a filter, not a guess. Ask for the state at time T, filter edges where T falls inside the window, done. On paper it's cleaner than a decay curve because there's no fuzziness. A fact is either valid at T or it isn't. Schema-wise, it was simple enough. In a property graph it looks like this: // A temporal fact: Node A was in maintenance for a fixed window MATCH ( n: Server { name: 'node-a' }) CREATE ( n ) - [ :HAS_STATE { status: 'maintenance' , valid_from: datetime ( '2026-07-20T02:00:00Z' ), valid_to: datetime ( '2026-07-20T04:30:00Z' )
AI 资讯
The web’s newest weapon against AI scrapers is a font
“ShieldFont” aims to poison AI training data without making pages unreadable for people.
AI 资讯
Most "big budget" clipping campaigns never pay. Here's how to spot them from one scrape
If you clip short-form video for money, you know Whop Content Rewards: hundreds of live campaigns paying $0.15–$20 per 1,000 views. The discover page lets you sort by budget. That sort is quietly costing you nights of work. Here's the number that changed how I pick campaigns: on the live board right now, 21% of active campaigns have never paid out a single cent. Big banner budget, $0 actually spent. A "$30,000 budget" campaign that has paid nobody in three weeks is not a $30,000 opportunity — it's a landing page. The problem: the board doesn't show you payout speed. You can see budget and budget left , but not how fast the money is actually moving — and that's the only number that separates a campaign that pays from a campaign that poses. The trick: the page already contains everything you need Every campaign card on Whop publishes three things: when it was funded, how much has been spent, and how many creators joined. From one snapshot — no monitoring, no state between runs — you can derive: dailyBurnUsd = budgetSpent / daysSinceFunded → is money moving? estimatedDaysLeft = budgetLeft / dailyBurnUsd → will it still be there? payoutPerCreatorUsd = budgetSpent / creators → what did the average clipper earn? budgetPace = "draining" | "healthy" | "slow" | "stalled" That last field is the shortcut. On today's board of 456 campaigns: pace meaning what to do draining <3 days of budget left skip — gone before your clip gains traction healthy 3–60 days this is where you clip slow 60–180 days fine, but budget may outlive the campaign stalled >180 days at current burn the "big budget" mirage — money posted, almost nobody paid null zero paid out so far unproven; could be brand new, could be dead Real example from today: two campaigns, both showing ~$30K budget. One burns $255/day and has paid the average creator $75 . The other burns $19/day — at that rate its budget lasts four years , which is a polite way of saying nobody is getting paid. On the default board they look ident
开发者
Python Now Has a Post-Quantum Encryption Library
This is good : Post-quantum cryptography is now one pip-install away for the entire Python ecosystem. With funding from the Sovereign Tech Agency , we implemented support for ML-KEM, the NIST-standard key-establishment primitive, and ML-DSA, the NIST-standard digital-signature primitive, in pyca/cryptography. Remember, the reason to do this now is because there’s no emergency. And because you will make your systems crypto agile, which is always a good idea.
AI 资讯
Stripe Uses Graph Search and State Machines to Automate Database Remediation
The engineering team at Stripe recently described how they automated database incident recovery by modeling their global infrastructure as a graph. Using graph search algorithms together with state machines, the team computes and executes remediation plans automatically. By Renato Losio
AI 资讯
An Empty VAST Wrapper Is Schema-Valid in 4.4. It Was Not in 2.0.
A VAST wrapper with no AdSystem, no VASTAdTagURI and no Impression validates against the VAST 4.4 draft schema. The same document has been invalid in every version from 2.0 through 4.2. It is one line of XSD, and it is almost certainly a side effect of the CTV Ad Portfolio restructure rather than a decision anyone made on purpose. I have filed it with IAB Tech Lab. This post is the working, because the reproduction is short enough that anyone can check it in about a minute. The change In vast_4.4.xsd on master, both vastInLine_type and vastWrapper_type wrap their children in a single compositor: an xs:choice with minOccurs zero and maxOccurs unbounded. That looks harmless. It is the idiom people reach for when they want to say "these children may appear in any order". What it actually says is stronger than that. In XSD, the cardinality on the compositor governs the content model, and the minOccurs on the individual child elements only describes a single selection from the choice. Set the choice itself to zero-or-more and every constraint underneath it stops binding. So the children still declare minOccurs="1". They are still, in effect, optional. The compositor in question <!-- vast_4.4.xsd, vastWrapper_type and vastInLine_type --> <xs:choice minOccurs= "0" maxOccurs= "unbounded" > <xs:element name= "AdSystem" type= "vastAdSystem_type" /> <xs:element name= "VASTAdTagURI" type= "vastURIElement_type" /> <xs:element name= "Impression" type= "vastImpression_type" /> <xs:element name= "Creatives" type= "vastCreatives_type" /> <!-- ... --> </xs:choice> Three consequences, not one The empty wrapper is the headline, but the compositor gives up three separate guarantees at once. Each is reproducible with xmllint against the published schema. What now validates in 4.4 Everything is optional. An empty <Wrapper/> validates. So does an empty <InLine/> , with no AdSystem, no AdTitle, no Impression and no Creatives. Everything repeats. maxOccurs="unbounded" on the choice means any
AI 资讯
Zero Knowledge Proofs: How to Win Every "Trust Me Bro" Argument With Math
A tutorial where you prove things without revealing things, and yes, the math actually maths. Here's something the internet doesn't want you to know: you overshare every single time you prove something. Prove you're over 21 at a bar? You hand over a card with your name, your address, your height, and your terrible 2019 haircut. Prove your income to a landlord? Here's every transaction I've made since college, please don't judge the 3am food delivery. We built the entire digital world on a verification model that boils down to "here's everything, trust me bro." Not anymore. There's a branch of cryptography that lets you prove a statement is true while revealing nothing else . It sounds fake. It's called a zero knowledge proof , and by the end of this article you'll understand one well enough to check it with Python. Then we'll look at Midnight , a blockchain that turned this party trick into a developer platform. Let's go. 🚀 🪪 The Trust Me Bro Problem Every verification system you use today works by disclosure . You prove things by showing the underlying data: Prove your age ➡️ show your whole ID Prove you can pay ➡️ show your bank statements Prove you're a real user ➡️ solve a CAPTCHA and sacrifice your data to the algorithm gods The data doesn't just get seen . It gets stored , and eventually it gets breached , and then a guy named xX_darkweb_Xx is selling your identity for the price of a burrito. The verifier never needed the data. They needed one bit of information : true or false. Everything else was collateral damage. In short: we've been answering yes or no questions with our entire life story. 🕵️ The Party Trick That Started It All Zero knowledge proofs let a prover convince a verifier that a statement is true without revealing why it's true. The classic example is Where's Waldo. Say I claim I found Waldo on the page and you don't believe me (fair, you've seen my code reviews). I could point at him, but then I've revealed the answer and ruined the puzzle. Ins
AI 资讯
Beyond the Password: How Passkeys Work Under the Hood
Passwords are broken. They get leaked in database breaches, reused across platforms, and phished through clever domain spoofs. While Multi-Factor Authentication (MFA) helps, standard SMS or OTP codes still leave major security gaps. Enter Passkeys : a modern authentication standard built on top of WebAuthn and FIDO2 specifications designed to eliminate shared secrets entirely. Here is a dive into the underlying cryptography, architectural flow, and why passkeys are inherently phishing-proof. The Core Concept: Asymmetric Cryptography Traditional authentication relies on shared secrets . Both you and the server know your password (or a hashed version of it). To verify who you are, you send that secret over the network. Passkeys replace shared secrets with asymmetric (public/private key) cryptography : Private Key: Generated locally on your device and stored inside a Hardware Security Module (like Apple's Secure Enclave, Android's Titan chip, or a YubiKey) or an end-to-end encrypted sync service (iCloud Keychain, 1Password, Google Password Manager). It never leaves your device unencrypted. Public Key: Registered with and stored by the website (the Relying Party). It is completely public and mathematically useless to an attacker on its own. Architectural Breakdown: How It Works Passkey operations consist of two cryptographic phases: Registration and Authentication . Phase 1: Registration (Generating the Keypair) When you create a passkey for a service (e.g., app.example.com ): Challenge Request: Your client initiates registration. The server generates a cryptographic challenge (a high-entropy random string) and sends it back alongside origin metadata ( RP ID ). Local Verification: Your browser hands this request to the OS/authenticator, which prompts for user verification—biometrics (Face ID/Touch ID/Windows Hello) or a hardware PIN. KeyPair Generation: Once unlocked, the hardware generates a unique keypair bound exclusively to app.example.com . Public Key Registration:
AI 资讯
Honeytokens that recognise themselves: stateless decoys with automatic attribution
Every signal in the previous articles is statistical. They weigh evidence, they have thresholds, they can be argued with. Honeytokens are different in kind. A honeytoken is a record that does not exist and was never given to anyone . Nothing legitimate can ask for it, because nothing legitimate has ever held a reference to it. A request for one isn't suspicious — it's proof that someone is guessing or working from a stolen list. That makes it the highest-confidence signal available, and worth building carefully. Derivation: make the decoy recognise itself The obvious implementation is a table. Generate decoy IDs, store them, and check every miss against the table. That has two problems. It puts a database lookup in the request path on every miss — and misses are exactly what a flood produces. And it doesn't tell you whose decoy was tripped without another join. Instead, derive them: export function honeytokenFor ( clientId : string , n : number , generation = 0 ): string { const mac = createHmac ( ' sha256 ' , config . honeytokenSecret ) . update ( ` ${ clientId } : ${ generation } : ${ n } ` ) . digest (); return uuidFromBytes ( mac . subarray ( 0 , 16 )); } Three properties fall out of this, and they're the whole design: Recognition is stateless. Given any ID and any client, recompute the client's decoy set and check membership. No lookup, no cache, no round trip. The gateway precomputes each known client's set at startup into a Set and membership is O(1). Attribution is automatic. The client ID is inside the derivation. There is no "which client did this decoy belong to?" question — a decoy for integration-acme is not a decoy for anyone else, and cannot be. If a decoy seeded into acme's scope is requested by a different credential, that's information too. They're format-identical to real IDs. The output is shaped as a v4 UUID — correct version and variant nibbles — so it is indistinguishable from a real documents identifier: export function uuidFromBytes ( bytes
AI 资讯
Immich vs Google Photos: Why Self-Hosting Your Photo Library Wins in 2026
Immich is the better choice if you own a machine that stays powered on and you care where your photos live. It gives you the parts of Google Photos people actually use every day, mobile auto backup, face grouping, map view, albums and shared links, without a storage meter that raises your bill as your library grows. Google Photos still wins on zero maintenance and on search that understands a sentence. If you are willing to spend one evening on setup and roughly an hour a quarter on updates, Immich replaces it. TL;DR by reader profile: Family archivist with 15 years of photos (Marta, two phones, one shared library): move to Immich on a small always on box, because a growing archive is exactly the case where a per gigabyte subscription compounds against you forever. Photographer shooting RAW every weekend (Tomas, 40 megapixel bodies): Immich, because RAW files eat cloud tiers fast and you already keep a local working copy that you can point the server at. Non technical user with one phone and no home server (Elena, iPhone, no NAS): stay on Google Photos for now, because Immich needs someone to own updates, backups and remote access, and that someone would be you. Privacy sensitive professional handling client images (lawyer, therapist, journalist): Immich on hardware you control, because the legal question is not whether the provider is trustworthy but who can be compelled to hand over the data. Homelab owner already running Docker (Sam, existing NAS and reverse proxy): Immich, because the marginal cost is one compose stack on infrastructure you maintain anyway. Small team or studio sharing a shoot library (five people, one archive): Immich with per user accounts and shared albums, because Google Photos was built for one person and gets awkward the moment several people need write access. The central tradeoff: Google Photos sells you freedom from maintenance and pays for it with a recurring bill and a library you do not control, while Immich hands you control and a o
AI 资讯
Every Way to Export LinkedIn and Sales Navigator Data (and When Each One Actually Works)
A few months back I was running Sales Navigator searches for a client project — filtering down to "VP Sales, fintech, based in Italy or Spain" type lists — and the results were genuinely good. 60, 80 leads that actually matched. Then I hit the part nobody warns you about: there's no button on that page that says "save this." So I did what everyone does. Opened a spreadsheet, alt-tabbed back and forth, typed names and job titles by hand. Around profile 40 I gave up and went looking for a better way. This is what I found, roughly in the order I found it, including the tool I ended up building because none of the existing options quite fit what I needed. First: the export LinkedIn actually gives you LinkedIn has a real, built-in data export, and most people don't realize how narrow it is. It's under your profile photo → Settings & Privacy → Data Privacy → Get a copy of your data . From there you either tick specific categories (that email usually lands within minutes) or request the full archive, which takes closer to a day and sometimes arrives in two batches. Either way you get a download link that expires after 72 hours — and it's desktop only, the mobile app won't let you request one. What you get back is genuinely thorough: connections, messages, your own profile history, activity, even the ad-targeting data LinkedIn holds on you. A couple of quirks worth knowing before you rely on it: some connections' email addresses will just be missing, because sharing an email on download is something each person opts into individually, and you won't get a list of who viewed your profile or any "People You May Know" data. If you're in the EU, EEA, or Switzerland, LinkedIn also runs a separate API for pulling your data on a schedule rather than as a one-off request. Here's what this export is not built for, though: it has no idea what you searched for yesterday. It's an archive of your own account, not a way to capture a live search. Run a Sales Navigator query and pull 80 lea
开发者
Sellar un archivo para que nadie pueda discutir que no lo tocaste
Una discusión sobre un archivo digital casi nunca se pierde por lo que el archivo dice. Se pierde una pregunta antes: ¿Cómo sabemos que ese es el archivo que usted recibió, y no el que editó anoche? Si la respuesta es "confíe en mí", ya perdiste. Y da igual cuánta razón tengas en el fondo. Este problema no es exclusivo de un juzgado. Lo tiene el auditor que recibe un volcado de logs, el equipo que documenta un incidente, quien conserva la copia de un contrato firmado por correo. En todos los casos hace falta lo mismo: poder demostrar que un conjunto de bytes no cambió desde un momento determinado, y que lo demuestre alguien que no seas tú . Para eso escribí Tunjo : una herramienta en Rust que recorre un material en solo lectura, calcula su huella y firma un acta verificable por cualquiera. Por qué un árbol y no un hash Lo obvio sería concatenar todo y sacar un SHA-256. Funciona, y es inútil en la práctica. Cuando alguien discute un archivo —un correo concreto entre cuatro mil— con un hash único solo puedes ofrecer dos cosas: o entregas el conjunto completo para que se recalcule, o pides que te crean. La primera opción expone material que no tiene por qué exponerse; la segunda no es una prueba. Un árbol de Merkle resuelve exactamente eso. Cada archivo es una hoja, cada par de nodos se combina hacia arriba y queda una raíz. Para demostrar que una hoja pertenece a esa raíz basta con exhibir esa hoja y el camino de hashes hasta arriba: unos pocos kilobytes. El resto del conjunto no se toca. Dos detalles del árbol que no son opcionales: // Separación de dominio: una hoja nunca puede hacerse pasar por nodo interno. h . update ([ 0x00 ]); // hoja h . update ([ 0x01 ]); // nodo interno // Y la raíz ata el número de hojas. h . update ([ 0x02 ]); h . update ( n . to_be_bytes ()); Sin lo primero, un hash de hoja podría presentarse como si fuera un nodo del árbol. Sin lo segundo aparece la ambigüedad clásica de los árboles con número impar de hojas: dos conjuntos distintos pued
开发者
Cómo solucionar el error “Enable JavaScript and cookies to continue”
Cómo solucionar el error “Enable JavaScript and cookies to continue” Este error aparece cuando Cloudflare (u otro proxy inverso de seguridad) detecta que el navegador del usuario no cumple con los requisitos mínimos para acceder al sitio: JavaScript está deshabilitado o las cookies no están permitidas . Pero en entornos reales, el problema suele ser más sutil: el navegador sí tiene JS y cookies habilitados, pero la configuración del entorno de ejecución (como un headless browser, test automation, o un scraper) no emula correctamente el comportamiento del cliente . 🔍 Causa raíz técnica Cloudflare emite un desafío (CAPTCHA o JS challenge) para verificar que el cliente es un navegador real. Si la respuesta no cumple con el desafío (por ejemplo, porque: El navegador no ejecuta el JS del desafío (headless sin soporte), Las cookies no se persisten entre solicitudes, El User-Agent o Accept-Language no coinciden con navegadores reales, Falta el Referer o Origin en headers, Se bloquean cookies de terceros (como las de Cloudflare), … entonces el servidor devuelve este mensaje estático en lugar de redirigir a la página solicitada. ⚠️ Nota crítica : Si estás usando herramientas como curl , requests de Python, o navegadores headless sin configuración especial, no pasarás el desafío de Cloudflare . Es intencional: Cloudflare bloquea tráfico no humano por diseño. ✅ Solución definitiva (por escenario) 🛠️ Caso 1: Navegador real (usuario final) Verifica que JavaScript esté habilitado : Chrome: Configuración → Privacidad y seguridad → Configuración de sitios → JavaScript → Permitido . Firefox: Preferencias → Privacidad y seguridad → Cookies y datos de sitios → Deshabilitar “Bloquear cookies y datos de sitios” . Limpia cookies y caché (especialmente para *.cloudflare.com ). Reinicia el navegador y vuelve a cargar la página. 🛠️ Caso 2: Automatización / Scraping (Python + Playwright/Selenium) No uses requests o urllib : no ejecutan JS. Usa un navegador real con soporte para Cloudflare. ✅