<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Practical Tech by Kamran]]></title><description><![CDATA[Practical Tech by Kamran]]></description><link>https://kamranbuilds.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Practical Tech by Kamran</title><link>https://kamranbuilds.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 19 Sep 2026 18:10:26 GMT</lastBuildDate><atom:link href="https://kamranbuilds.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Built a WordPress Broken Link Checker for 500-Page Audits]]></title><description><![CDATA[Broken links are easy to fix once you know where they are. The difficult part is finding them across a growing website, separating genuine failures from restricted responses, and showing enough contex]]></description><link>https://kamranbuilds.hashnode.dev/wordpress-broken-link-checker-500-page-audit</link><guid isPermaLink="true">https://kamranbuilds.hashnode.dev/wordpress-broken-link-checker-500-page-audit</guid><category><![CDATA[WordPress]]></category><category><![CDATA[SEO]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Developer Tools]]></category><dc:creator><![CDATA[Kamran Liaquat Ali]]></dc:creator><pubDate>Tue, 15 Sep 2026 09:25:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6aa28b2d4f3dcbf5d82c7627/09b90f67-44c7-46ca-b8b2-49697824bd12.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Broken links are easy to fix once you know where they are. The difficult part is finding them across a growing website, separating genuine failures from restricted responses, and showing enough context for someone to make the right correction. I built a WordPress-based checker to make that review process more practical.</p>
<p>This article explains the decisions behind the tool, how its audit workflow is organized, and why an automated result should be treated as evidence to review rather than an instruction to delete a link.</p>
<h2>The maintenance problem I wanted to solve</h2>
<p>A single article may contain only a handful of links, but a publication with hundreds of pages can contain thousands of internal and external destinations. Over time:</p>
<ul>
<li><p>Pages are deleted or renamed.</p>
</li>
<li><p>Permalink structures change.</p>
</li>
<li><p>External documentation moves.</p>
</li>
<li><p>Domains expire.</p>
</li>
<li><p>Temporary server failures appear.</p>
</li>
<li><p>Security systems block automated requests.</p>
</li>
</ul>
<p>Opening every page and testing every link manually does not scale. I wanted a tool that could automate discovery and status checking while keeping the final decision with the website owner.</p>
<p>The resulting <a href="https://proarticleshub.com/broken-link-checker/">PAH Broken Link Checker</a> can audit up to 500 eligible public pages and organize the detected links into useful status groups.</p>
<h2>Start with the website, not a random page</h2>
<p>The tool expects a website homepage as its starting point. From there, it looks for a public XML sitemap and collects eligible same-domain page URLs.</p>
<p>This approach has two practical benefits:</p>
<ol>
<li><p>It gives the audit a defined scope.</p>
</li>
<li><p>It avoids blindly crawling every URL that can be reached through navigation, calendars, filters, or other potentially unlimited paths.</p>
</li>
</ol>
<p>Sitemaps are not perfect inventories, but they are usually a sensible starting point for a repeatable website audit.</p>
<h2>Separate page discovery from link checking</h2>
<p>The workflow has two distinct stages.</p>
<p>First, the checker discovers and retrieves eligible website pages. It then extracts standard HTTP or HTTPS links from their HTML.</p>
<p>Second, it deduplicates destination URLs and checks their responses. Deduplication matters because the same navigation, footer, legal, or reference link may appear across many pages. Checking identical destinations repeatedly would slow the audit and make the final report harder to review.</p>
<p>The report also keeps a discovered source page for each destination. A broken URL without context is less useful than a broken URL accompanied by a page where it was found.</p>
<h2>A non-200 response is not always broken</h2>
<p>One of the most important design decisions was to avoid treating every non-200 response as the same problem.</p>
<h3>Successful responses</h3>
<p>A <code>2xx</code> response generally means the destination was reachable during the scan. It does not guarantee that the content is still relevant, but it normally requires no immediate technical correction.</p>
<h3>Redirects</h3>
<p>Responses such as <code>301</code>, <code>302</code>, <code>307</code>, and <code>308</code> are shown separately. Redirects are normal when pages move, so they are not automatically classified as broken.</p>
<p>For an important internal link, it may still be worth updating the source to point directly to the final destination. That reduces unnecessary redirect hops and keeps the website's internal references current.</p>
<h3>Restricted responses</h3>
<p>The checker separates <code>401</code>, <code>403</code>, and <code>429</code> responses into a restricted category.</p>
<p>These codes can appear because a destination requires authentication, blocks automated traffic, applies firewall rules, or rate-limits repeated requests. A normal browser may still be able to open the page, so deleting the link automatically would be the wrong response.</p>
<h3>Broken responses</h3>
<p>Responses such as <code>404</code> and <code>410</code> usually indicate that the resource is unavailable at the requested address. Other <code>4xx</code> or <code>5xx</code> results may also require investigation, depending on their meaning and whether the failure persists.</p>
<h3>Request errors</h3>
<p>Timeouts, DNS failures, and refused connections do not always prove that a page is permanently unavailable. They are reported separately so the user can retry or verify the destination from another network.</p>
<h2>Why manual verification still matters</h2>
<p>Automated link checkers interact with websites differently from human visitors. Bot protection, JavaScript rendering, authentication, regional restrictions, and temporary outages can all affect a result.</p>
<p>My preferred workflow is:</p>
<ol>
<li><p>Filter the audit to broken or uncertain results.</p>
</li>
<li><p>Open the reported source page to understand the link's purpose.</p>
</li>
<li><p>Test the destination in a normal browser.</p>
</li>
<li><p>Confirm whether the failure is permanent.</p>
</li>
<li><p>Correct, replace, redirect, or remove the link based on context.</p>
</li>
<li><p>Run the audit again after making changes.</p>
</li>
</ol>
<p>This avoids turning a useful diagnostic tool into an automatic content-deletion system.</p>
<h2>Choosing the right fix</h2>
<p>Different failures require different actions.</p>
<table>
<thead>
<tr>
<th>Situation</th>
<th>Sensible response</th>
</tr>
</thead>
<tbody><tr>
<td>Typo in the destination</td>
<td>Correct the URL in the source page</td>
</tr>
<tr>
<td>Internal page permanently moved</td>
<td>Update the link and create a relevant permanent redirect if needed</td>
</tr>
<tr>
<td>External reference moved</td>
<td>Replace it with the current authoritative URL</td>
</tr>
<tr>
<td>Content intentionally removed</td>
<td>Remove the link or return a genuine 404/410 when no replacement exists</td>
</tr>
<tr>
<td>Destination returns 403 or 429</td>
<td>Verify manually before editing anything</td>
</tr>
<tr>
<td>Temporary 5xx or timeout</td>
<td>Retry later before deciding</td>
</tr>
</tbody></table>
<p>A redirect should lead to a genuinely relevant replacement. Sending every missing page to the homepage may hide the error without helping the visitor.</p>
<h2>Building it for WordPress</h2>
<p>The published implementation is written in PHP and designed for WordPress. It can be integrated through the included shortcode:</p>
<pre><code class="language-text">[pah_broken_link_checker]
</code></pre>
<p>The interface provides status filters, report search, source-page information, and CSV export so results can be reviewed in Excel or Google Sheets.</p>
<p>The source implementation is publicly available in the <a href="https://github.com/kamranliaquatali-sys/pah-broken-link-checker">PAH Broken Link Checker GitHub repository</a>. The repository also includes contribution guidance, a security policy, issue templates, and notes about responsible reporting.</p>
<p>The code is source-available for review and improvement. No open-source license is granted unless a license file explicitly says otherwise, so users should review the repository's ownership terms before reuse.</p>
<h2>Limitations worth documenting</h2>
<p>Clear limitations make an auditing tool more trustworthy.</p>
<ul>
<li><p>The current scan is limited to 500 eligible pages.</p>
</li>
<li><p>Page discovery depends on a readable public sitemap.</p>
</li>
<li><p>The scanner extracts standard anchor links present in retrieved HTML.</p>
</li>
<li><p>Links created only after complex JavaScript execution may be missed.</p>
</li>
<li><p>Some destinations intentionally block automated requests.</p>
</li>
<li><p>The stored source page is a discovered occurrence, not a complete backlink report.</p>
</li>
<li><p>A successful HTTP response cannot determine whether the page's content is accurate or relevant.</p>
</li>
</ul>
<p>These limitations do not make the audit useless. They define what the results can reliably tell you.</p>
<h2>Final takeaway</h2>
<p>A broken-link audit works best when automation and human judgment are combined. Automation can discover pages, collect destinations, test responses, remove repetition, and highlight likely problems. A person still needs to understand why the link exists and choose the appropriate fix.</p>
<p>That balance shaped the PAH Broken Link Checker: automate repetitive inspection, preserve useful context, and avoid pretending that every unusual HTTP response has the same meaning.</p>
]]></content:encoded></item><item><title><![CDATA[How to Generate and Understand a Windows Battery Report with PowerShell]]></title><description><![CDATA[A laptop can show 100% charge and still provide much less runtime than it did when it was new. The charge percentage only tells you how full the battery is right now. It does not tell you how much cap]]></description><link>https://kamranbuilds.hashnode.dev/windows-battery-report-powershell-guide</link><guid isPermaLink="true">https://kamranbuilds.hashnode.dev/windows-battery-report-powershell-guide</guid><category><![CDATA[Windows]]></category><category><![CDATA[Powershell]]></category><category><![CDATA[windows 11]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Kamran Liaquat Ali]]></dc:creator><pubDate>Thu, 10 Sep 2026 11:09:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6aa28b2d4f3dcbf5d82c7627/c353e27a-fffe-41ec-917e-ddb3e3a08a23.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A laptop can show 100% charge and still provide much less runtime than it did when it was new. The charge percentage only tells you how full the battery is right now. It does not tell you how much capacity the battery can still hold.</p>
<p>Windows includes a detailed battery report that helps answer that question. You can generate it with one PowerShell command, then compare the battery's current full-charge capacity with its original design capacity.</p>
<p>This guide explains how to create the report, find the important values, and interpret them without confusing charge level with battery health.</p>
<h2>Generate the Windows battery report</h2>
<p>Open PowerShell and run:</p>
<pre><code class="language-powershell">powercfg /batteryreport /output "$env:USERPROFILE\Desktop\battery-report.html"
</code></pre>
<p>Windows should confirm that it saved the report to your Desktop. Open <code>battery-report.html</code> in any browser.</p>
<p>The same command works on Windows 10 and Windows 11. Administrator access is usually unnecessary, although device policies on a managed work computer may restrict the command.</p>
<h2>Use a small PowerShell helper</h2>
<p>If you generate reports regularly or do not want to remember the command, I published a small open-source helper:</p>
<p><a href="https://github.com/kamranliaquatali-sys/windows-battery-report-helper">Windows Battery Report Helper on GitHub</a></p>
<p>After downloading <code>Generate-BatteryReport.ps1</code>, run it from PowerShell:</p>
<pre><code class="language-powershell">.\Generate-BatteryReport.ps1
</code></pre>
<p>It creates <code>battery-report.html</code> on the Desktop by default. To generate and open the report immediately, use:</p>
<pre><code class="language-powershell">.\Generate-BatteryReport.ps1 -OpenReport
</code></pre>
<p>The script only calls the built-in Windows <code>powercfg</code> utility. It does not upload the generated report.</p>
<h2>The three values that matter most</h2>
<p>Windows battery reports contain several tables, but three values provide the clearest starting point.</p>
<h3>Design capacity</h3>
<p>Design capacity is the amount of energy the battery was rated to hold when new. Windows normally displays it in milliwatt-hours (mWh).</p>
<p>Treat this as the original reference value, not the amount the battery holds today.</p>
<h3>Full charge capacity</h3>
<p>Full charge capacity is the maximum amount of energy the battery currently reports it can hold. Normal chemical aging usually causes this figure to decrease over time.</p>
<p>For example:</p>
<ul>
<li><p>Design capacity: 50,000 mWh</p>
</li>
<li><p>Full charge capacity: 40,000 mWh</p>
</li>
</ul>
<p>The battery currently holds about 80% of its original rated capacity.</p>
<h3>Cycle count</h3>
<p>A charge cycle represents cumulative use equal to 100% of the battery's capacity. Two separate 50% discharges can therefore add up to approximately one cycle.</p>
<p>Some manufacturers do not expose cycle count to Windows. A blank cycle count does not necessarily mean the battery has completed zero cycles.</p>
<h2>Calculate battery health percentage</h2>
<p>Use this formula:</p>
<pre><code class="language-text">Battery health (%) = Full charge capacity / Design capacity × 100
</code></pre>
<p>Using the earlier example:</p>
<pre><code class="language-text">40,000 / 50,000 × 100 = 80%
</code></pre>
<p>This figure is better described as reported capacity health. Battery firmware readings can fluctuate slightly after charging, calibration, firmware updates, or changes in temperature.</p>
<h2>What the percentage can and cannot tell you</h2>
<p>Capacity health helps estimate how much runtime the battery has lost compared with its original specification. It is not a complete safety test and should not be treated as a guaranteed diagnosis.</p>
<p>Pay attention to physical warning signs even if the percentage appears acceptable. Stop using the laptop and seek qualified assistance if the battery is swollen, leaking, unusually hot, producing an odor, or deforming the case.</p>
<p>Runtime also depends on screen brightness, processor load, background applications, wireless activity, power settings, and temperature. A healthy battery can still drain quickly under a heavy workload.</p>
<h2>Check capacity history instead of one reading</h2>
<p>The <code>Battery capacity history</code> section shows how full-charge capacity changed over time. A gradual decline is expected. A sharp drop may be worth checking against recent firmware changes, usage conditions, or a second report generated after a few normal charge cycles.</p>
<p>One reading is useful, but a trend is more informative.</p>
<h2>Make the report easier to read</h2>
<p>The standard HTML report is comprehensive, but its tables can be difficult to interpret. I built <a href="https://batteryhealthlab.com/">Battery Health Lab</a> to turn the report into a simpler health summary.</p>
<p>The analyzer runs in the browser, requires no account, and processes the selected report locally on the device. You can also enter design capacity and full-charge capacity manually if you prefer not to select the HTML file.</p>
<h2>Common problems</h2>
<h3>“Unable to perform operation”</h3>
<p>Confirm that you are running the command on a Windows laptop with a detected battery. Try opening a fresh PowerShell window and use an explicit output path such as the Desktop example above.</p>
<h3>The report opens but contains no battery</h3>
<p>Open Device Manager and check whether Windows detects the battery under <strong>Batteries</strong>. Desktop computers and some virtual machines do not expose battery data.</p>
<h3>PowerShell blocks the helper script</h3>
<p>You can use the direct <code>powercfg</code> command instead. If you change PowerShell's execution policy, understand the security implications and avoid applying a broad permanent change only to run one script.</p>
<h2>Final takeaway</h2>
<p>Generating a Windows battery report is simple; interpreting it correctly is the important part. Compare full-charge capacity with design capacity, examine the history rather than relying on one reading, and consider real-world runtime and physical condition alongside the percentage.</p>
<p>That combination provides a much clearer picture of a laptop battery than the taskbar charge indicator alone.</p>
]]></content:encoded></item></channel></rss>