Exporting data to csv in WordPress with PHP

In WordPress plugin development, sometimes you may need to export data to a csv file. Here we share how to implement it with PHP code.

How to export a csv file with PHP code

header() is used to send a raw HTTP header. To export a generated csv file, use below headers to tell the browser display a save dialog.

header( 'Content-Type: text/csv' ); // Supply the mime type
header( 'Content-Disposition: attachment; filename="downloaded.pdf"' ); // Supply a file name to save


There is not official RFC document for CSV files. text/csv is not a standard mime type, but it is more clear and works fine. application/octet-stream can also be used for csv files. However it is a very generous and it does not hint which application should be used to open the file.

Use two more headers to tell the content should not be cached by browser or any caches between the server and the browser.

header( "Cache-Control: no-cache, must-revalidate" );
header( "Expires: Sat, 26 Jul 1997 05:00:00 GMT" ); // Date in the past

Then echo the generated csv content directly:

echo $csv;


Any actual content must be output after header(). One common error is outputting content before header() by include or require a file. See more about header().

Below is the full example.

Example: Export data to csv in WordPress

Add an export button:

<a href="admin.php?page=export_example&export=table&noheader=1">Export</a>

Don’t forget add the argument named “noheader”. Otherwise, the csv content will directly be printed and there will be no Save dialog box popping up in the browser.

Process the export in the same file:

$table_head = array( 'column1', 'column2', 'column3' );
$table_body = array(
    array( 'a', 'b', 'c' ),
    array( 'd', 'e', 'f' )

// Process export
if( isset( $_GET['export'] ) ) {
    $csv = implode( $table_head, ',' );
    $csv .= "n"; // important! Make sure to use use double quotation marks.
    foreach( table_body as row ) {
        $csv .= implode( $row, ',' );
        $csv .= "n";

    $filename = 'table.csv';    
    header( 'Content-Type: text/csv' ); // tells browser to download
    header( 'Content-Disposition: attachment; filename="' . $filename .'"' );
    header( 'Pragma: no-cache' ); // no cache
    header( "Expires: Sat, 26 Jul 1997 05:00:00 GMT" ); // expire date

    echo $csv;
<a href="admin.php?page=export_example&export=table&noheader=1">Export</a>

This example works well on Firefox, IE, Chrome.

Export data with Unicode characters to utf-8 bom csv

If your data contains Unicode characters, you may want to export a utf-8 bom csv that Excel will read properly. To achieve that, just output the BOM mark before data:

echo "xEFxBBxBF"; // UTF-8 BOM
echo $csv;

Fixing JetPack related posts not showing for XMLRPC parse error

JetPack related posts are not showing, WordPress says This site cannot be accessed, and xmlrpc test shows parse error. not well formed . Here it shows you how to fix it.

Test your site

Run below command to test your site in an terminal (Any computer is fine.)

$ curl -A "Jetpack by WordPress.com" -d "<methodCall><methodName>demo.sayHello</methodName></methodCall>" https://www.your damain.com/xmlrpc.php

The correct result should be:

<?xml version="1.0" encoding="UTF-8"?>

It is a XMLRPC parse error if the response is:

<?xml version="1.0" encoding="UTF-8"?>
          <value><string>parse error. not well formed</string></value>

Tips: You can also test your site using https://xmlrpc.eritreo.it/. Just input your site and click Check button. Other information like username and password is not needed.

If it is an XMLRPC parse error, the result will be:

Code      Description
-32700    parse error. not well formed

Solution for parse error

Connect to your website host, run below command to install php-xml:

$ sudo apt-get install php-xml

If your host does not have apt-get installed, you may need use (Such as AWS EC2 with Amazon Linux 2):

$ sudo yum install php-xml

Retest your site, it should be OK (Restart your apache sever if needed). After your posts are synced to WordPress site, you may see related posts.

Note: Related posts will not appear unless at least 3 good related posts can be found by JetPack. See more on jetpack related posts.

Tips: For other JectPack connection issues,fix them according to fixing jetpack connection issues.




  • Partial clone

    How to clone only part of commits of a huge repository



  • Stash changes

    How to save changes temporally, apply changes, list stashes, delete a stash?




  • Remote management

    How to add a remote, delete a remote, change the URL of a remote, etc.


  • Differences

    How to check differences between working tree, index, a specific commit?




  • Check log

    How to view the last several commits, search the commit history with string, list the commits graphically, check which commit changed a string, etc.

Deleting files from Git commit history

If you commits an sensitive file or a huge unwanted file, you may want to remove it from every commit. Git provides a nuclear-level command git filter-branch which allows you rewrite the history.

git filter-branch executes the specified command for each commit specified by you and generates new commits.

Before you start, you must keep it in mind that this operation changes the existing history. If it is a public repository and someone have did some work based on the commits you want to rewrite, you’d better not do this. If you have to, remember to notify them to run git pull --rebase command.

Delete a huge folder from every commit

Here is an example of how to remove a huge folder from each commit which is committed accidentally at first.

You’d better test below commands in a temporary repository to make sure that they work properly for your git version. It is met that git filter-branch below removes the folder in working tree as well but it should not.

We do it in a new testing branch, when the result is what we want then reset it as the prior branch.

# Do it in a new testing branch
$ git checkout -b test

# Remove 'build' folder from every commit on the new branch
# --index-filter, rewrite index without checking out
# -r, remove recursively in subfolders
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# --prune-empty, remove empty commits generated by 'git rm' command
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch build' --prune-empty HEAD
Rewrite fee4b8ee9df321a877cd2663b20b293eea4a1f8c (1/2)rm 'build/main.app'
Rewrite 63f272ab5152c66693614efae77567799837c6e0 (2/2)
Ref 'refs/heads/test-filter' was rewritten

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -rm test

# Push it with force
$ git push --force origin master

If you changed commits in remote repository, remember notice other members execute below command:

# Tell others to execute below command if you changed commits in remote repository.
$ git pull --rebase


  1. If --ignore-unmatch option is not added, it will fail when the files to be removed do not exist in a commit.
  2. The files you removed will stay in disk for a while, they will be removed entirely in the next automic garbage collection of git.

Tips: To avoid adding unwanted files accidently, you should ignore it.

Other useful options:

# Execute the specified command for the last 5 commit
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch user.pem' HEAD~6..HEAD

# Execute the specified command for all branches
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch user.pem' -- --all

# Update tags when executing filter-branch, remember to push them to remotes afterwards
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch user.pem' --tag-name-filter cat HEAD

# Remove empty commits generated by 'git rm' command
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch user.pem' --prune-empty HEAD

Remotes management in Git

In Git, you can have more than one remote repository. git remote command is used to manage them. Operations illustrated include how to add a remote, remove a remote, view a remote’s information, change a remote’s URL, etc.

Add a remote

# Add a remote
$ git remote add <name> <url>

# Examples:
# Add a remote named test
$ git rmeote add test https://github.com/username/repository.git

Note: When you clone a remote repository, Git sets the remote as origin for you automatically.

List remotes

# Show remotes with names
$ git remote

# Show remotes with names, urls
$ git remote -v
origin    git@github.com:mojombo/grit.git (fetch)
origin    git@github.com:mojombo/grit.git (push)

Change URL of a remote

# Chang url of origin
$ git remote set-url origin https://github.com/USERNAME/REPOSITORY.git

Verify its URL has changed:

$ git remote get-url origin
origin https://github.com/USERNAME/REPOSITORY.git

Get URL of a remote

# Get the url of origin
$ git remote get-url origin
origin https://github.com/USERNAME/REPOSITORY.git

Fetch updates from a remote

# Fetch updates from the remote named origin
$ git remote update origin

Rename a remote

# Rename a remote
$ git remote rename <old> <new>

# Examples:
# Rename origin to main
$ git remote rename origin main

Remove a remote

# Remove a remote
# Note: all the remote-tracking branches and settings 
# for the remote are removed.
$ git remote remove <name>

# Examples:
# Remove the remote nameed server2
$ git remote remove server2