Webdev tech notes
(2013–    ) ·


return to topPort forwarding with a Comcast Business router

[macOS 12.3 Monterey]

My Comcast Business router appears to disallow port forwarding to LAN addresses that fall outside the range of DHCP addresses. For example, if you set up an ssh server with a fixed IP address, and try to set port forwarding to that address, you get the error: "Unable to Edit". One workaround described elsewhere on the web (open DHCP to all ports, restart router, assign port forwarding to the fixed IP server, then set the DHCP range to exclude the server, then restart the router) does not work: ssh cannot make a connection. This is all very annoying and inexplicable. But there is a workaround:

  1. Set your ssh server to a fixed IP address that's within the range of DHCP addresses
  2. Go to Connected Devices > Devices and click "ADD DEVICE WITH RESERVED IP".
  3. Enter the info. (For this you'll need the server's MAC address. To get it, type: ifconfig en1 | awk '/ether/{print $2}'.)
  4. Click SAVE.

You should now be able to connect to the server remotely.

return to topmacOS Monterey: Installing virtual hosts, PHP, https

[macOS 12.1 Monterey]

As of macOS 12 (Monterey), Apple no longer provides built-in PHP. You have to install it yourself. The simplest solution is to install a different version of httpd. It's not scary to do; it just requires patience and a bunch of small steps. So pour yourself a cup of tea and dive in...

This is a thumbnail view of how I installed PHP, set up virtual hosts, and activated SSL for a local development webserver. It worked for me. YMMV, etc. You can find many useful tidbits at other good tutorials out there (see end of this article).


  • Experience with the command line and a text editor, etc., etc.
  • Experennce running httpd on previous versions of macOS.
  • Your website files are in the directory /Users/USERNAME/Sites
1. Install httpd from brew (see brew.sh for details).
At the Terminal:
sudo apachectl stop
brew install httpd

Brew's httpd file locations:
httpd config : /opt/homebrew/etc/httpd/ (macos default: private/etc/apache2/)
web server default root : usr/local/var/www/ (macos default: /Library/WebServer/Documents)
httpd log files : /opt/homebrew/var/log/httpd/

Handy commands:

brew services start httpd or sudo apachectl start
brew services stop httpd or sudo apachectl stop
brew services restart httpd or sudo apachectl graceful
sudo apachectl configtest
2. Install php
brew install php # installs latest version (8.1)
3. Edit /etc/hosts for virtual hosts:
Add this line:	MYHOSTNAME
Where "MYHOSTNAME" is the name of your host — i.e., you'd point your browser to "https://MYHOSTNAME". Can be just about anything, e.g., "mysite" or "dev.whatever.com".
4. Set up config files for virtual hosts and SSL

Make a backup copy of the main config file:

cd /opt/homebrew/etc/httpd/
cp httpd.conf httpd.conf.bak
Edit http.conf:
Listen 80
Uncomment these modules:
LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
LoadModule include_module lib/httpd/modules/mod_include.so
LoadModule ssl_module lib/httpd/modules/mod_ssl.so
LoadModule vhost_alias_module lib/httpd/modules/mod_vhost_alias.so
LoadModule userdir_module lib/httpd/modules/mod_userdir.so
LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so
Add this:
LoadModule php_module /opt/homebrew/opt/php/lib/httpd/modules/libphp.so
<FilesMatch \.php$>
	SetHandler application/x-httpd-php
Change the appropriate lines to these:
ServerName localhost
DocumentRoot "/Users/USERNAME/Sites"
<Directory "/Users/USERNAME/Sites">
Options Indexes FollowSymLinks Multiviews
AllowOverride All
Uncomment these lines:
Include /opt/homebrew/etc/httpd/extra/httpd-userdir.conf
Include /opt/homebrew/etc/httpd/extra/httpd-vhosts.conf

Make a backup copy of the virtual host config file:

cd extra
cp httpd-vhosts.conf httpd-vhosts.conf.bak
Edit httpd-vhosts.conf. It should look something like this:
	DocumentRoot /Users/USERNAME/Sites/MYSITEDIR
	ErrorLog "/opt/homebrew/var/log/httpd/MYHOSTNAME-error_log"
	CustomLog "/opt/homebrew/var/log/httpd/MYHOSTNAME-access_log" common
Now test the configuration so far.
sudo apachectl configtest
Address any errors it reports. Point your browser to http://MYHOSTNAME . If it works, great. If not, look at the log files:
tail /opt/homebrew/var/log/httpd/MYHOSTNAME-error_log
tail /opt/homebrew/var/log/httpd/MYHOSTNAME-access_log

5. Get ssl working.
Edit httpd.conf and uncomment this line:
Include /opt/homebrew/etc/httpd/extra/httpd-ssl.conf
Create SSL certificate:
cd /opt/homebrew/etc/httpd
mkdir ssl
cd ssl
brew install mkcert
brew install nss  # do this to please Firefox
mkcert -install	#installify the certificate
mkcert MYHOSTNAME  # Make certificate/key pair for the host
You should now have two files in the dir:
Edit httpd-vhosts.conf and add the following:
	DocumentRoot /Users/jtb/Sites/MYSITEDIR
	ErrorLog "/opt/homebrew/var/log/httpd/MYHOSTNAME-ssl-error_log"
	CustomLog "/opt/homebrew/var/log/httpd/MYHOSTNAME-ssl-access_log" common
	SSLEngine on
	SSLCertificateFile /opt/homebrew/etc/httpd/ssl/MYHOSTNAME.pem
	SSLCertificateKeyFile /opt/homebrew/etc/httpd/ssl/MYHOSTNAME-key.pem
Edit extra/httpd-ssl.conf:
Listen 443
and comment out the entire <VirtualHost>...</VirtualHost> block.
6. Test it
sudo apachectl configtest
Address any errors it reports.

Point your browser to https://MYHOSTNAME . If it works, great. If not, look at the log files:

tail /opt/homebrew/var/log/httpd/MYHOSTNAME-ssl-error_log
tail /opt/homebrew/var/log/httpd/MYHOSTNAME-ssl-access_log

Add a file ("phptest.php") to your website's root directory:

<html><body><?php phpinfo() ?></body></html>
Point your browser to https://MYHOSTNAME/phptest.php If you see the php info, you're all set. If not, put on your debugging hat...

6. Other tutorials and sources of useful info:

return to topRestoring Apache after macOS updates

[macOS 11.2.1 Big Sur]

Whenever Apple issues a major macOS update, it overwrites many of your custom Apache config files, which means that it's up to you to manually restore each affected file. Yes, it's annoying, but it's manageable. Fortunately, Apple saves your pre-update Apache files to the "Relocated items" folder, an alias of which will be installed on your Desktop folder.

These are the files that I usually need to restore:

/etc/hosts  [rarely]

When I notice that Apache is broken after an update, I recover copies of my previous Apache config files, either from the "Relocated Items" folder or from my Time Machine backup, and use BBEdit's bbdiff command-line tool to merge my settings into the new files.

After editing the files as desired, double-check your work:

% sudo apachectl configtest

Then restart Apache:

% sudo apachectl graceful

(Sometimes you might have to do that twice.) Then reload your browser page.

If that doesn't work, quit and re-launch browser.

If that doesn't work, purge the browser cache of your virtual hosts, then quit and re-launch your browser.

return to topSetting up a reverse proxy server on macOS 10.15 (Catalina)

[macOS 10.15.7 Catalina]

These notes are about setting up a reverse proxy server so that you can, for example, stream audio from another website.

Why might you need a reverse proxy server? If you're trying to deliver content that originates from a server other than yours, you may run into problems with cross-origin resource sharing (CORS). For perfectly valid security reasons, some shared web hosting platforms (Dreamhost, for example), block their hosted sites from re-sharing certain kinds of data that originate from other sites. If you're running your own web server, however, you can configure Apache yourself to allow CORS, by setting up a reverse proxy.

In this example, we'll set up virtual host "FOO" so that it can play audio from one of the streams at the Earthsound Project.

1. Activate the proxy server modules by uncommenting these lines in /etc/apache2/httpd.conf :

LoadModule xml2enc_module libexec/apache2/mod_xml2enc.so LoadModule proxy_html_module libexec/apache2/mod_proxy_html.so LoadModule proxy_module libexec/apache2/mod_proxy.so LoadModule proxy_connect_module libexec/apache2/mod_proxy_connect.so LoadModule proxy_http_module libexec/apache2/mod_proxy_http.so LoadModule proxy_ajp_module libexec/apache2/mod_proxy_ajp.so LoadModule proxy_balancer_module libexec/apache2/mod_proxy_balancer.so

2. Define your reverse proxy virtual host by editing /etc/apache2/extra/httpd-vhosts.conf :

<VirtualHost *:80> ServerName FOO DocumentRoot /Users/USERNAME/Sites/FOO ErrorLog "/Users/USERNAME/Sites/apache2-log/FOO-error_log" CustomLog "/Users/USERNAME/Sites/apache2-log/FOO-access_log" common ProxyPreserveHost On ProxyPass /ffc ProxyPassreverse /ffc </VirtualHost>

(Here ffc stands for the audio stream from seismic station FFC in Flin Flon, Canada.)
3. Test your Apache config and restart :

$ apachectl configtest ... $ apachectl graceful $

4. Test the proxy :

Point your browser to http://FOO/ffc/stream. If all went well, you should hear the audio feed from seismic station FFC in Canada. With a little javascript, you can now apply html5's <audio> element to this audio feed, to create all sorts of interesting audio and visual effects.

return to topQuickies for macOS sysadmin

Enable Rosetta to run legacy code on Apple Silicon hardware $ softwareupdate --install-rosetta $
Show CPU info $ sysctl -a | grep machdep.cpu machdep.cpu.cores_per_package: 8 machdep.cpu.core_count: 8 machdep.cpu.logical_per_package: 8 machdep.cpu.thread_count: 8 machdep.cpu.brand_string: Apple M1 $
Show operating system version $ sw_vers ProductName: macOS ProductVersion: 11.6 BuildVersion: 20G165 $
Get IP addresses and MAC ids of machines on the local network $ arp -a ... $
Show my external network IP address: $ curl ifconfig.me $ OR THIS: $ dig -4 TXT +short o-o.myaddr.l.google.com @ns1.google.com "" $
Copy file from a remote server
(copy remote file 'foo' to local 'bar')
$ scp -r username@ localpath/to/bar $
Basic Apache commands (macOS): $ sudo apachectl configtest (to test syntax of config files) $ sudo apachectl start $ sudo apachectl graceful (to restart ) $ sudo apachectl stop $ sudo apachectl -M (list loaded modules) $
Basic Apache commands (Debian/Ubuntu): $ sudo apachectl configtest (to test syntax of config files) $ sudo service apache2 start $ sudo service apache2 restart $ sudo service apache2 stop $ sudo apachectl -M (list loaded modules) $

return to topSetting up SSL virtual hosts on macOS 10.15 (Catalina)

[macOS 10.15.7 Catalina]

These notes are about setting up SSL with virtual hosts on your local development machine. They assume you already have two virtual hosts ("FOO" and "BAR") up and running on Apache.

1. Activate SSL by uncommenting a few lines in /etc/apache2/httpd.conf:
Change #ServerName localhost
To: ServerName localhost

Change #LoadModule socache_shmcb_module libexec/apache2/mod_socache_shmcb.so
To: LoadModule socache_shmcb_module libexec/apache2/mod_socache_shmcb.so

Change #LoadModule ssl_module libexec/apache2/mod_ssl.so
To: LoadModule ssl_module libexec/apache2/mod_ssl.so

Change #Include /private/etc/apache2/extra/httpd-ssl.conf
To: Include /private/etc/apache2/extra/httpd-ssl.conf

2. Create SSL certificates :

$ sudo mkdir /etc/apache2/ssl $ cd /etc/apache2/ssl $ sudo openssl genrsa -out FOO.key 2048 Password: Generating RSA private key, 2048 bit long modulus (2 primes) .........+++++ ........................+++++ e is 65537 (0x010001) $ sudo openssl req -new -x509 -key FOO.key -out FOO.crt -days 3650 -subj /CN=FOO $

Repeat the above to create certs for virtual host "BAR".

I'm not sure if this next step is really necessary, but WTH:

$ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /etc/apache2/ssl/FOO.crt $ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /etc/apache2/ssl/BAR.crt $

3. Comment out the <VirtualHost> section in /etc/apache2/extra/httpd-ssl.conf :

(Since we have more than one virtual host, with more than one cert, it's easier to customize each vhost in our httpd-vhosts.conf.)

Edit /etc/apache2/extra/httpd-ssl.conf and change this:

<VirtualHost _default_:443> # General setup for the virtual host DocumentRoot "/Library/WebServer/Documents" ServerName www.example.com:443 ... [many, many lines] ... CustomLog "/private/var/log/apache2/ssl_request_log" \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" </VirtualHost>


# <VirtualHost _default_:443> # # # General setup for the virtual host # DocumentRoot "/Library/WebServer/Documents" # ServerName www.example.com:443 ... [many, many lines] ... # CustomLog "/private/var/log/apache2/ssl_request_log" \ # "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" # # </VirtualHost>

4. Edit /etc/apache2/extra/httpd-vhosts.conf :

Change this:

<VirtualHost *:80> ServerName FOO DocumentRoot /Users/USERNAME/Sites/PATH_TO_FOO ErrorLog "/Users/USERNAME/Sites/apache2-log/FOO-error_log" CustomLog "/Users/USERNAME/Sites/apache2-log/FOO-access_log" common </VirtualHost>


<VirtualHost *:80> ServerName FOO DocumentRoot /Users/USERNAME/Sites/PATH_TO_FOO ErrorLog "/Users/USERNAME/Sites/apache2-log/FOO-error_log" CustomLog "/Users/USERNAME/Sites/apache2-log/FOO-access_log" common </VirtualHost> <VirtualHost *:443> ServerName FOO DocumentRoot /Users/USERNAME/Sites/PATH_TO_FOO ErrorLog "/Users/USERNAME/Sites/apache2-log/FOO-ssl-error_log" CustomLog "/Users/USERNAME/Sites/apache2-log/FOO-ssl-access_log" common SSLCertificateFile /etc/apache2/ssl/FOO.crt SSLCertificateKeyFile /etc/apache2/ssl/FOO.key </VirtualHost>

Make similar changes to virtual host "BAR".

5. Test it and start Apache :

$ apachectl configtest ... $ apachectl graceful $

Once it's running, you can check the apache settings thus:

$ httpd -S $

6. Launch a browser and override browser warnings :

Each of these urls should now work: http://FOO , https://FOO , http://BAR , https://BAR .

Most browsers will object to the self-signed certificate, and will therefore block access to your site. Since this is your own server, it's safe to override the warning:

  • Firefox : "Warning: Potential Security Risk Ahead" (MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT) ⇒ Advanced ⇒ "Accept the Risk and Continue"
  • Chrome : "Your connection is not private" (ERR_CERT_COMMON_NAME_INVALID) ⇒ Advanced ⇒ Proceed
  • Safari : "This connection is not private" ⇒ "If you understand the risks involved, you can visit this website"

You'll probably also want to add mod_rewrite rules to your .htaccess files in order to redirect http to https.

return to topGetting Apache webserver to work on macOS 10.14 (Mojave)

[OS 10.14.4 Mojave]

After upgrading from High Sierra to Mojave, my local webserver stopped working. Pointing Chrome at one of my virtual hosts yielded a "This site can’t be reached" error. Time for a little digging...

$ sudo apachectl configtest AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using helios.local. Set the 'ServerName' directive globally to suppress this message Syntax OK $ sudo apachectl -k restart /usr/sbin/apachectl: line 92: 14084 Segmentation fault: 11 $HTTPD "$@" $

Although the syntax of the config files is reportedly ok, something is terribly wrong. Let's start by getting rid of that pesky AH00558 error. Edit /private/etc/apache2/httpd.conf and insert a line with the directive ServerName localhost . Try again:

$ sudo apachectl configtest Syntax OK $ sudo apachectl -k restart /usr/sbin/apachectl: line 92: 14084 Segmentation fault: 11 $HTTPD "$@" $

Clearly, it's still broken. Turns out the cause is straightforward: Apache is trying to load two competing PHP modules: php7 (in httpd.conf) and php5 (in /private/etc/apache2/other/+php-osx.conf). Fix: Open /private/etc/apache2/other/+php-osx.conf and comment out the line that loads the php5 module. The file should now look like this:

# LoadModule php5_module /usr/local/php5/libphp5.so <IfModule mod_php5.c> AddType application/x-httpd-php .php AddType application/x-httpd-php-source .phps <IfModule mod_dir.c> DirectoryIndex index.html index.php </IfModule> </IfModule>

Restart Apache and you're good to go.

$ sudo apachectl -k restart $

return to topUpgrading to PHP 7

[OS 10.10.5 Yosemite]

Upgrading to PHP 7 on macOS 10.10 (Yosemite) did not go smoothly. The installation script from LIIP almost worked, but failed in its final steps. But it's not hard to patch things up. Here's what worked for me (kudos to Neil Gee's Coolest Guides on the Planet and several of his readers' comments).

The LIIP installation script yielded this:

% curl -s https://php-osx.liip.ch/install.sh | bash -s 7.0 Detected OS X Yosemite 10.10. All ok. Get packager.tgz Unpack packager.tgz Please type in your password, as we want to install this into /usr/local Password: Start packager (may take some time) downloading https://s3-eu-west-1.amazonaws.com/php-osx.liip.ch/install/7.0-10.10-... downloading https://s3-eu-west-1.amazonaws.com/php-osx.liip.ch/install/7.0-10.10/... Installing package 7.0-10.10-frontenddev into root / ./pkg/pre-install pkg/pre-install Skipping existing directory Skipping existing directory usr/ Skipping existing directory usr/local/ Extracting usr/local/php5-7.0.11-20160923-203451/ ... Extracting usr/local/php5-7.0.11-20160923-203451/bin/xsltproc Executing post-install script /tmp/7.0-10.10-frontenddev-post-install rm: /usr/local/php5: is a directory cp: /usr/local/php5/lib/php.ini-development: No such file or directory Create symlink /usr/local/php5/entropy-php.conf /etc/apache2/other/+php-osx.conf Restarting Apache httpd: Syntax error on line 537 of /private/etc/apache2/httpd.conf: Syntax error on line 8 of /private/etc/apache2/other/+php-osx.conf: Cannot load /usr/local/php5/libphp5.so into server: dlopen(/usr/local/php5/libphp5.so, 10): Symbol not found: _environ\n Referenced from: /usr/local/php5/libphp5.so\n Expected in: /usr/sbin/httpd\n %

This results in a broken httpd install. To confirm that it's broken:

% apachectl configtest httpd: Syntax error on line 537 of /private/etc/apache2/httpd.conf: Syntax error on line 8 of /private/etc/apache2/other/+php-osx.conf: Cannot load /usr/local/php5/libphp5.so into server: dlopen(/usr/local/php5/libphp5.so, 10): Symbol not found: _environ\n Referenced from: /usr/local/php5/libphp5.so\n Expected in: /usr/sbin/httpd\n %

To fix it:

  1. Save the old PHP directory and create a symlink to the new one

    % cd /usr/local % sudo mv php5 php5-myoldversion % sudo ln -s php5-7.0.11-20160923-203451 php5 %

  2. Edit php5/entropy-php.conf and change this line:

    LoadModule php5_module /usr/local/php5/libphp5.so

    to this:

    LoadModule php7_module /usr/local/php5/libphp7.so

  3. To verify that it's working:

    % apachectl configtest Syntax OK %

  4. Restart Apache:

    % sudo apachectl graceful %

  5. Finally, to use the correct PHP command line interface, edit your .bashrc or .cshrc, etc., to make sure that the $PATH environment variable gives /usr/local/php5/bin precedence over /usr/local/bin/. This ensures that you'll be using the right one:

    % which php /usr/local/php5/bin/php % php -v PHP 7.0.11 (cli) (built: Sep 23 2016 20:33:19) ( NTS ) Copyright (c) 1997-2016 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies with Zend OPcache v7.0.11, Copyright (c) 1999-2016, by Zend Technologies with Xdebug v2.4.1, Copyright (c) 2002-2016, by Derick Rethans %

Welcome to the brave new world of PHP 7.

return to top Sending email from PHP scripts

[OS 10.10.2 Yosemite]

Sending email from PHP forms can be tricky. PHP's built-in mail() function works OK if the webhost (a) has sendmail set up correctly and (b) it isn't frequently blacklisted by SpamHaus (booo, DreamHost!). But even then, it can be a tad cumbersome to set up all of mail()'s arguments. The simplest and most portable solution is the class PHPMailer.

Setting up PHPMailer is a breeze ‐ especially when using a SMTP server such as Gmail's. Here's how:

  1. Download PHPMailer.
  2. Install these three files into your site: class.phpmailer.php, class.smtp.php, and class.pop3.php .
  3. The following sample code, adapted from Felipe Alameda A's Stackoverflow answer, works for me.

    include_once("class.phpmailer.php"); include_once("class.smtp.php"); include_once("class.pop3.php"); $mail = new PHPMailer(); $mail->IsSMTP(); // Use SMTP $mail->Host = "smtp.gmail.com"; // Sets SMTP server $mail->SMTPDebug = 2; // 2 to enable SMTP debug information ( delete this line when it's running ok) $mail->SMTPAuth = TRUE; // enable SMTP authentication $mail->SMTPSecure = "tls"; //Secure conection $mail->Port = 587; // set the SMTP port $mail->Username = 'USERNAME@gmail.com'; // SMTP account username $mail->Password = 'PASSWORD'; // SMTP account password $mail->Priority = 3; // Email priority (1 = High, 3 = Normal, 5 = low) $mail->CharSet = 'UTF-8'; $mail->Encoding = '8bit'; $mail->ContentType = 'text/html; charset=utf-8\r\n'; $mail->From = 'YOU@YOURDOMAIN.COM'; $mail->FromName = 'GMail Test'; $mail->WordWrap = 900; // RFC 2822 Compliant for Max 998 characters per line $mail->AddAddress("SOMEONE@SOMEDOMAIN.COM"); // To: $mail->isHTML( TRUE ); $mail->Subject = 'Test Email Using Gmail'; $mail->Body = $MessageHTML; $mail->AltBody = $MessageTEXT; $mail->Send(); $mail->SmtpClose();

PHPMailer's error messages (generated when you set SMTPDebug) are very helpful. Read them carefully.

See also this tech note: "Send email from command line via Gmail SMTP in Mac OS X Yosemite"

return to top Notes on ensuring that your HTML is also valid XML

Some tips collected from around the Internet:

Entities that are predefined in XML:

EntityRendered as

Entities that must be converted to their numerical form:

EntityRendered asNumeric character reference
&copy; © &#x00A9;
&mdash; &#x2014;
&sect; § &#x00A7;
&rarr; &#x21D2;

See Wikipedia's List of XML and HTML character entity references.

return to top Notes on migrating from HTML4 to HTML5

Some tips collected from around the Internet:

  1. Move <table> attributes cellspacing, cellpadding and <td> attributes align, valign to CSS:
    // cellpadding th, td { padding: 5px;} // cellspacing table { border-collapse: separate; border-spacing: 5px; } // cellspacing="5" table { border-collapse: collapse; border-spacing: 0; } // cellspacing="0" // valign th, td { vertical-align: top; } // align (center) table { margin: 0 auto; }

    solution provided by user "drudge" at StackOverflow.com

  2. In <script> elements, eliminate type='text/javascript' and language='javascript' attributes.
  3. In <img /> elements, move align='...' to CSS.

return to top Quickie: setting up mySQL & PhpMyAdmin

Note: For a much simpler, clearer, better tutorial on all this, please see Jeff Moore's article "Working with PHP 5 in Mac OS X 10.5 (Leopard)".

These notes are based on my experience installing and configuring mySQL and phpMyAdmin on my Mac (Dual PowerPC G5 / OS X 10.4.11). Maybe they’ll also be helpful to you. They are no substitute for the "official" installation instructions. They are strictly "as-is". Use at your own risk. Mileage may vary, etc.

  1. Make sure your local web server is turned on. (Open the "Sharing" panel in your System Preferences, and make sure the "Personal Web Sharing" item is checked. Note: this is where you can also change your computer’s name.)
  2. Point your browser to http://localhost . If you haven’t already configured a local website, you’ll get the default Apache web page (~/Sites/index.html).
Installing mySQL
  1. Get the latest version.
  2. Double-click the .dmg file to mount the mySQL disk image
  3. There are two mySQL packages in the disk image file. Double-click the one named mysql-foo.pkg, where foo contains this mySQL version number and your OS.
  4. Follow the installer instructions. This installs mySQL in /usr/local/mysql
  5. Add /usr/local/mysql/bin to your shell’s search path (if you don’t know how to do this, see the ReadMe.txt file).
  6. In Terminal:

    % sudo /usr/local/mysql/bin/mysqld_safe # (enter password, if asked) % ^Z # suspend the daemon % bg # resume daemon as a background task

  7. Create mysql’s "root" user. In Terminal:

    % sudo mysqladmin -u root password somePassword

    Here "somePassword" is a password for mysql’s "root" user. This "root" is not the same as the system’s "root" user. Go figure. Read Apple’s docs for more details.

  8. Test the installation. In Terminal:

    % mysql -u root somePassword # test mysql

    This should spit out a list of mysql options and variable settings. If not, you’ll have to find some more in-depth docs. (Start with the ReadMe.txt.) Sorry.

Installing PhpMyAdmin
  1. Get the latest version.
  2. Unzip the file into ~/Sites .
  3. This creates a directory with a long and hard-to-remember name. Rename it phpMyAdmin.
  4. In Terminal:

    % cd ~/Sites/phpMyAdmin % mkdir config # create directory for saving % chmod o+rw config # give it world writable permissions

  5. Point your browser to http://localhost/~YOURUSERNAME/phpMyAdmin
  6. You should see a "Welcome to phpMyAdmin" page. It will probably contain an "Access denied" error message because you haven’t created a config file. Click on the link to the "setup script".
  7. In the "Servers" row, click the "Add" button. To avoid having to enter a password each time you run phpMyAdmin, enter the mySQL "root" password in the "Password for config auth" field. You can accept all the other defaults. Click the green "Add" button.
  8. Click the "Save" button in the "Configuration" row.
  9. In terminal:

    % cd ~/Sites/phpMyAdmin % mv config/config.inc.php .

    The phpMyAdmin docs go further, and ask you to do this:

    % chmod o-rw config.inc.php # remove world read and write permissions

    But when I do that, phpMyAdmin is denied access. Since I’m hiding behind a firewall I’m not going to worry about those permissions right now....

If you point your browser to http://localhost/~YOURUSERNAME/phpMyAdmin you might get this error message:

MySQL said: #2002 - The server is not responding (or the local MySQL server’s socket is not correctly configured)

If so, do the following (courtesy of Ben VanScoter):

% sudo mkdir /var/mysql % sudo ln /tmp/mysql.sock /var/mysql/mysql.sock % sudo apachectl graceful /usr/sbin/apachectl graceful: httpd gracefully restarted %

Works for me.

Now you’ll be able to create and manage mySQL databases using phpMyAdmin, to your heart’s content.

return to topQuickie: using svn for website development

After years of dithering, I finally made the switch to using version control to manage my websites. Looking back, I can only wonder what took me so long. Now I use Subversion. I am happy. Why? Here are some reasons:

  • It provides a seamless way for several people to collaborate on the same project without stepping on each other’s toes. (Have you ever had a collaborator delete one of your crucial files?)
  • You can easily document the changes you make to your code.
  • If you make a mistake, You can easily "roll back" to an earlier version.

Making the switch to Subversion is well worth the modest effort required. (If I can do it, so can you.) This "Quickie" article will take you through (1) creating an online repository for your site; (2) creating a development site for doing live testing on your live server; and (3) using Subversion in routine development work. There are, doubtless, other, better, ways to accomplish all this, but this is what has worked for me.

Assumptions in this article

  • You have a basic grasp of what Subversion is, what it’s for, and (roughly) how to use it
  • Subversion is installed on your server and on your local computers
  • You have a site (we’ll call it www.example.com) that’s already up and running on your Apache server. I’ll also assume your site lives in the server directory ~/example.com (i.e., in your home directory)
  • You have login (shell) access to the server


For starters, you’ll need to create two new sub-domains. Follow your web host’s instructions about how to do that:

  1. Create two new sub-domains: svn.example.com (for your svn repository) and dev.example.com (for your development site). [If you’re using DreamHost, go to your webpanel and select Domains » Manage Domains » Add New Domain/Sub-Domain.] It’s a good idea to password-protect your dev site, so that only you and your co-developers have access to your not-quite-ready-for-prime-time version of the site.
  2. Create a new Subversion project. We’ll give it the id "myproject", and install it to the svn.example.com URL. (Don’t forget the username and password you choose here!) [If you’re using DreamHost, you can create a new Subversion project right from your webpanel: select Goodies » Subversion.]

The rest of the setup happens through the login shell on your server. So log on to your server and do the following:

  1. To hide the Subversion directories from web browsers, create (or edit) the .htaccess file in ~/dev.example.com. Add the following line:

    RedirectMatch 403 /\\.svn.*$

  2. Add this line also to the .htaccess file in ~/www.example.com.
  3. Create an empty trunk/branch/tag directory structure as the initial import into the project. (In the following, "YOURNAME" is your svn username.)

    % cd % mkdir tmp % cd tmp % mkdir branches trunk tags % svn import . --username YOURNAME -m "initial import" http://svn.example.com/myproject Authentication realm: <http://svn.example.com:80> My Project Password for ’YOURNAME’: {enter password} Adding trunk Adding branches Adding tags Committed revision 1. % cd .. % /bin/rm -fr tmp

  4. Now go ahead and import your current site (www.example.com) into the repository:

    % cd ~/www.example.com % svn import . --username YOURNAME -m "importing website" http://svn.example.com/myproject/trunk

    If your site contains some dirs that you want to exclude from the development version (e.g., wordPress), you might prefer to import the site on a directory-by-directory basis:

    % cd ~/www.example.com % svn import ./keep_me1 --username YOURNAME \\ -m "importing keep_me1 directory" http://svn.example.com/myproject/trunk/keep_me1 % svn import ./keep_me2 --username YOURNAME \\ -m "importing keep_me2 directory" http://svn.example.com/myproject/trunk/keep_me2

    Or, to be more efficient about it, you could run a little shell script like this:

    #!/bin/tcsh -f foreach d ( some_dir another_dir yet_another_dir ) { svn import $d --username YOURNAME -m "importing $d directory" http://svn.example.com/myproject/trunk/${d} }

  5. Checkout a working copy to the development site (dev.example.com):

    % svn co http://svn.example.com/myproject/trunk ~/dev.example.com

Your dev site (dev.example.com) now contains a full working copy of the latest revision of your site.

Routine workflow

  1. Checkout a working copy from the repository to your local machine. (I recommend the svnX GUI client for the Mac.)
  2. Develop and test your code, etc., on your local machine.
  3. At logical stages (say, when you fix a bug or finish writing a new function), commit your changes to the repository. Some good habits to practice: (1) Always update your working copy before each commit. Always jot down a meaningful comment with each commit. (Instead of saying "Fixed a bug", say "Fixed function Foo() in bar.php that had wrong mySQL query.")
  4. Log on to your server and update the development site:

    % svn up ~/dev.example.com

  5. View the dev site. You may discover bugs on the dev site that weren’t apparent on your local machine. (They might be using different versions of Apache, PHP, mySQL, etc.) If there are bugs to be worked out, iron them out on your local working copy (i.e., go back to Step 2).
  6. If you’re satisfied that your dev site is good to go, then either checkout a copy to your live deployment site:

    % svn co http://svn.example.com/myproject/trunk ~/www.example.com

    or update the copy already in your deployment site:

    % svn up ~/www.example.com

  7. Double-check that the deployment site (http://www.example.com) is working to your satisfaction. If it’s not, go back to Step 2. If it is OK, you’re done!

The development cycle is now much simpler and safer. If you go on the road, just checkout a working copy onto your laptop, make your changes, and commit them back to the repository when you get the chance. If you (or your collaborator) made a mistake 6 revisions ago, just tell svn to checkout or update to an earlier revision.


return to topUse ssh/sftp instead of telnet/ftp

If you use telnet to log into your remote server, or ftp to transfer files between your local and remote machines, don’t. Use ssh instead; it’s much more secure. And if you’re accustomed to typing in a username and password when you login with ssh, you’re only creating more work for yourself. It only takes two minutes to set things up properly on your local and remote machines to permit secure non-passworded sessions using public/private key authentication. Do it today! Here’s how:

  1. On your local system do this:

    % ssh-keygen -t rsa

    When asked, just go with the default settings. This will generate your public key (a long string of text) and automatically insert it into the file ~/.ssh/id_rsa.pub.

  2. Now log into your remote system and open (and, if necessary, create) the file ~/.ssh/authorized_keys. Copy the public key that was generated on your local machine (e.g., % cat ~/.ssh/id_rsa.pub) and paste it at the end of this file. Make this file readable only to you, by doing:

    % chmod 700 ~/.ssh/.

  3. Log out of your remote system.
  4. Connect to your remote system via ssh:

    % ssh yourname@yourremotesystem.com

    You should be greeted by something like this:

    The authenticity of host 'yourremotesystem.com' (xxx.xxx.xxx.xxx) can't be established. RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:.... Are you sure you want to continue connecting (yes/no)?

    Type 'yes' and RETURN. You should see something like this:

    Warning: permanently added 'yourremotesystem.com, xxx.xxx.xxx.xxx' (RSA) to the list of known hosts.

That's it! From now on you'll be able to login with ssh (and use sftp) with ease, without ever having to type in a password.

return to topDreamHost: can’t connect to webpanel

Problem:Unable to access Dreamhost’s (DH) webpanel from my home computer (Mac OS 10.4). When pointing my browser to panel.dreamhost.com I’d invariably get a "connection timed out" error. The output from traceroute suggested to me that the problem was at DH’s end:

% traceroute panel.dreamhost.com traceroute to panel.dreamhost.com (, 64 hops max, 40 byte packets 1 [...] 2 [...] 3 at-1-2-0-1719.core-rtr2.bos.verizon-gni.net ( 25.461 ms 26.321 ms 25.591 ms 4 so-0-2-0-0.bb-rtr2.bos.verizon-gni.net ( 25.988 ms 26.193 ms 31.568 ms 5 0.so-5-2-0.xl2.bos4.alter.net ( 32.311 ms 27.812 ms 27.315 ms 6 0.so-6-0-0.xl2.lax15.alter.net ( 121.418 ms 121.219 ms 120.668 ms 7 pos7-0.gw2.lax15.alter.net ( 111.140 ms 112.440 ms 112.517 ms 8 internapgige-gw.customer.alter.net ( 115.499 ms 112.022 ms 111.304 ms 9 border1.po2-bbnet2.ext1a.lax.pnap.net ( 112.983 ms 112.325 ms 112.211 ms 10 newdream-1.border1.ext1a.lax.pnap.net ( 347.647 ms 114.602 ms 208.394 ms 11 lb1.sd.dreamhost.com ( 114.535 ms 119.165 ms 119.207 ms 12 * * * 13 * * * 14 *^C %

DH tech support recommended that I flush my DNS cache:

% lookupd -flushcache

This didn’t help. After many more back-and-forths with tech support, including elevation to a "level two queue" (??), I finally tracked down the problem to my /etc/hosts file.

Cause: An invalid entry in my /etc/hosts file. Earlier this year DH had recommended that its customers add these lines to their /etc/hosts file, as insurance against DH’s DNS server going down: panel.dreamhost.com dreamhoststatus.com dreamhost.com www.dreamhost.com www.dreamhoststatus.com

The trouble is that DH had subsequently changed the IP address of panel.dreamhost.com, without my knowing it.

Fix: Comment out those lines and restart the DNS daemon:

% sudo vi /etc/hosts [to insert ’#’ in front of those lines...]

% sudo killall -HUP lookupd

Moral #1: if you’re going to add IP addresses to your /etc/hosts, it’s up to you to make sure those entries are up to date. If your ISP tells you to add such-and-such to that file, don’t count on them keeping you informed of any changes later on.

Moral #2: When troubleshooting DNS problems, always remember: if traceroute chokes on a remote server along the route, it might very well be a problem with your DNS configuration. Always check /etc/hosts first!

return to topNotes

More stuff I tend to forget...

Launch the MySQL daemon
% sudo /usr/local/bin/mysqld_safe [type ^Z] % bg
Stop the MySQL daemon
% mysqladmin shutdown -p [type in the password when prompted] % mysqld --skip-grant-tables
Change MySQL’s root password
% mysqladmin -u root -p password NEW_PASSWORD [type in the old password when prompted]
Backup a MySQL database
% mysqldump --opt -uUSERNAME -pPASSWORD -h HOSTNAME DBNAME > db.txt % zip db.zip db.txt

If you can’t remember USERNAME, PASSWORD, HOSTNAME, or DBNAME, you can find them in (for example) your WordPress wp-config.php file.

During installation phpMyAdmin says "#2002 — The server is not responding (or the local MySQL server’s socket is not correctly configured)":

I don’t claim to understand this one at all. But some combination of these seem to do the trick:

  1. Log on as root and create the file /etc/my.cnf. Insert these lines into the file:
    [client] socket=/var/mysql/mysql.sock [mysqld] socket=/var/mysql/mysql.sock
  2. Look at /etc/php.ini and find the line concerning "Default socket name for local MySQL connects". There should be nothing after the equals sign:
    mysql.default_socket =
  3. Execute phpinfo() and examine all the references to mysql. Look for any weirdnesses and inconsistencies there. Make sure the mysql socket names and directory paths all make sense with your php installation.
Check your PHP configuration
Create a text file containing the following single line of code:
<?php phpinfo() ?>
Name the file something useful like phpinfo.php and load it into your browser.